Android Service 小结

Android Service 小结

IntentService
  1. 创建
public class MyIntentService extends IntentService {


    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     */
    public MyIntentService() {
        //Used to name the worker thread, important only for debugging.
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String url = intent.getStringExtra("url");
        Log.i("MyIntentService", "onHandleIntent url:" + url);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("MyIntentService", "onHandleIntent end url:" + url);

    }
}
  1. AndroidManifest注册
        <service android:name=".service.MyIntentService" android:exported="false"/>
  1. 使用
Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.putExtra("url", "abcd" + count++);
                startService(intent);
  1. 测试结果
I/MyIntentService: onHandleIntent url:abcd0
2020-06-23 09:30:20.399 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent end url:abcd0
2020-06-23 09:30:20.403 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent url:abcd1
2020-06-23 09:30:23.405 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent end url:abcd1
2020-06-23 09:30:23.407 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent url:abcd2
2020-06-23 09:30:26.409 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent end url:abcd2
2020-06-23 09:30:26.412 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent url:abcd3
2020-06-23 09:30:29.414 21121-21175/com.aaaa.sdkdemo I/MyIntentService: onHandleIntent end url:abcd3
  1. 总结
  • 执行简单后台操作的首选方式
  • IntentService 在单线程后台运行操作
  • 工作请求依序运行。即发送多个工作请求,按顺序单个运行,不会并行运行
  • IntentService 上运行的操作无法中断。
  • IntentService 运行完成后自动停止服务。
JobIntentService
  1. 创建
public class MyJobIntentService extends JobIntentService {

    public static final int JOB_ID = 1000;

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        String url = intent.getStringExtra("url");
        Log.i("MyJobIntentService", "onHandleWork url:" + url);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("MyJobIntentService", "onHandleWork end url:" + url);

    }
}
  1. AndroidManifest注册
<service android:name=".service.MyJobIntentService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false"/>

如果不添加 android:permission="android.permission.BIND_JOB_SERVICE"会报错:

java.lang.IllegalArgumentException: Scheduled service ComponentInfo{...} does not require android.permission.BIND_JOB_SERVICE permission

  1. 使用
Intent serviceIntent = new Intent();
serviceIntent.putExtra("url", "aaa" + count++);
MyJobIntentService.enqueueWork(MainActivity.this, MyJobIntentService.class,
    MyJobIntentService.JOB_ID, serviceIntent);
  1. 测试结果
2020-06-23 10:22:27.246 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork url:aaa0
2020-06-23 10:22:30.253 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork end url:aaa0
2020-06-23 10:22:30.256 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork url:aaa1
2020-06-23 10:22:33.267 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork end url:aaa1
2020-06-23 10:22:33.273 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork url:aaa2
2020-06-23 10:22:36.317 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork end url:aaa2
2020-06-23 10:22:36.321 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork url:aaa3
2020-06-23 10:22:39.362 23370-23417/com.aaaa.sdkdemo I/MyJobIntentService: onHandleWork end url:aaa3
  1. 总结
  • 适用于执行后台任务;
  • 多个工作请求,按顺序单个运行;
  • Android O 及以上版本内部实现存在不同
    • Android O 之前,
      • 分发工作请求通过 Context.startService
      • 工作请求入队后会立刻执行;
      • service 可以无限期运行,运行时间越长,越容易被系统彻底杀死进程;
      • 在内存压力下,预期进程会被杀死,即使是最近启动的服务;
    • Android O 以及之后的版本
      • 分发工作请求是作为 job 通过 JobScheduler.enqueue 分发的;
      • 如果设备在 dozing 状态,会以 Job 的形式运行,将会遵守 JobScheduler 策略;Job 将不会立刻运行;可能延迟很多,在强大的内存压力下;
  • 使用 JobIntentService 需要以下权限: (运行在 Android O 时)
    • Manifest.permission.WAKE_LOCK
BoundService (扩展 Binder 类)

绑定服务是客户端-服务器接口中的服务器。借助绑定服务,组件(例如 Activity)可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。

  1. 创建
public class MusicService extends Service {

    private final MusicBinder binder = new MusicBinder();


    public class MusicBinder extends Binder {
        public MusicService getService() {
            return MusicService.this;
        }
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public void doSomething(int a) {
        Log.i("MusicService", "doSomething " + a);
    }

}
  1. AndroidManifest注册
        <service android:name=".service.MusicService" android:exported="false"/>
  1. 使用
public class MainActivity extends AppCompatActivity {

    int count = 0;

    private MusicService musicService = null;
    private boolean isBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_bound_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isBound) {
                    musicService.doSomething(20);
                }
            }
        });
    }


    @Override
    protected void onStart() {
        super.onStart();
        // bind to service
        Intent intent = new Intent(this, MusicService.class);
        boolean bindRet = bindService(intent, connection, Context.BIND_AUTO_CREATE);
        Log.i("Main", "bindRet:" + bindRet);
    }

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
            musicService = binder.getService();
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }

    };

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
    }
}
  1. 总结
  • 您可以创建同时具有已启动和已绑定两种状态的服务。换言之,可通过调用 startService() 启动服务,让服务无限期运行;此外,还可通过调用 bindService() 让客户端绑定到该服务。
  • 如果您确实允许服务同时具有已启动和已绑定状态,则在启动服务后,如果所有客户端均解绑服务,则系统不会销毁该服务。为此,您必须通过调用 stopSelf()stopService() 显式停止服务。
  • 尽管您通常应实现 onBind()onStartCommand(),但有时需同时实现这两种方法。例如,音乐播放器可能认为,让其服务无限期运行并同时提供绑定会很有用处。如此一来,Activity 便可通过启动服务来播放音乐,并且即使用户离开应用,音乐也不会停止。然后,当用户返回应用时,Activity 便能绑定到服务,重新获得回放控制权。
BoundService (使用 Messenger)
  1. 创建
public class LocalService extends Service {

    private Messenger mMessenger;


    static class LocalHandler extends Handler {
        private Context context;

        LocalHandler(Context context) {
            this.context = context;
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            Log.i("LocalService", "handleMessage msg:" + msg.obj);

        }
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mMessenger = new Messenger(new LocalHandler(this));
        return mMessenger.getBinder();
    }
}
  1. AndroidManifest注册
        <service android:name=".service.LocalService" android:exported="false"/>
  1. 使用
public class MainActivity extends AppCompatActivity {

    private Messenger localMessenger;
    private boolean isBoundLocal = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_bound_local_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isBoundLocal) {
                    Message message = Message.obtain();
                    message.obj = "local_msg";
                    try {
                        localMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }


    @Override
    protected void onStart() {
        super.onStart();

        // bind to local service
        Intent localIntent = new Intent(this, LocalService.class);
        if (!bindService(localIntent, localConnection, Context.BIND_AUTO_CREATE)) {
            unbindService(localConnection);
        }
    }

    ServiceConnection localConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            localMessenger = new Messenger(service);
            isBoundLocal = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBoundLocal = false;
        }

    };

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(localConnection);
    }
  1. 总结
  • 如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。借助此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

为接口使用 Messenger 比使用 AIDL 更简单,因为 Messenger 会将所有服务调用加入队列。纯 AIDL 接口会同时向服务发送多个请求,服务随后必须执行多线程处理。

对于大多数应用,服务无需执行多线程处理,因此使用 Messenger 即可让服务一次处理一个调用。

  • 只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。
  • bindService() 绑定为异步操作;通过ServiceConnection传递IBinder;
  • 如果bindService()方法返回“false”,则说明您的客户端未与服务进行有效连接。但是,您的客户端仍应调用 unbindService();否则,您的客户端将阻止服务在空闲时关闭。
  • 如果您使用 Intent 来绑定到 Service,请务必使用显式 Intent 来确保应用的安全性。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务会响应该 Intent,并且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),则系统会抛出异常。
  • 您应该始终捕获 DeadObjectException 异常,系统会在连接中断时抛出此异常。这是远程方法抛出的唯一异常。
  • 当 Activity 在后台处于停止运行状态时,若您仍希望其能接收响应,则可在 onCreate() 期间进行绑定,在 onDestroy() 期间取消绑定。请注意,这意味着您的 Activity 在整个运行过程中(甚至包括后台运行期间)均需使用服务,因此如果服务位于其他进程内,则当您提高该进程的权重时,系统便更有可能会将其终止。
管理绑定服务的生命周期

当取消服务与所有客户端之间的绑定时,Android 系统会销毁该服务(除非您还使用 onStartCommand() 启动了该服务)。因此,如果您的服务完全是绑定服务,则您无需管理其生命周期,Android 系统会根据它是否绑定到任何客户端代您管理。

不过,如果您选择实现 onStartCommand() 回调方法,则您必须显式停止服务,因为系统现已将其视为已启动状态。在此情况下,服务将一直运行,直到其通过 stopSelf() 自行停止,或其他组件调用 stopService()(与该服务是否绑定到任何客户端无关)。

此外,如果您的服务已启动并接受绑定,则当系统调用您的 onUnbind() 方法时,如果您想在客户端下一次绑定到服务时接收 onRebind() 调用,则可选择返回 true。onRebind() 返回空值,但客户端仍在其 onServiceConnected() 回调中接收 IBinder。下图说明这种生命周期的逻辑。

已启动且允许绑定的服务的生命周期:
已启动且允许绑定的服务的生命周期

参考地址: https://developer.android.com/guide/components/services

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值