一点感想
说感想也算是工作中的一些总结吧,做项目基本都会使用Service这一四大组件,服务和Activity通信便是我们经常需要处理的问题,这里我说一说我比较熟悉的几种方式:
①BroadcastReceiver形式,这个是我最开始使用的方式,但是效率特别低,尤其是数据量大的时候,其次需要注册广播,解绑等等工作需要做,反而觉得很繁琐;
②Hander方式,实际工作中我用的很少;
③EventBus形式,在没有出现问题之前,我一直使用这种方式,使用这种方式代码量实在很少,并且简单,唯一不足是消息类型太多时很难维护,但是后来在工作中发现消息频率过高会出现内存泄漏(Memory Leak),没有具体研究过EventBus源码,没查找过根源;
④接口形式,这也是我目前所使用的形式,效率很高,操作简单。
如何使用接口形式与Activity通信
①新建接口类IDataCallback
public interface IDataCallback {
void dataCallback(Object obj);
}
②新建MyBinder 继承 Binder
public class MyBinder extends Binder {
private MyService mService;
public MyService getService() {
return this.mService;
}
public void setService(MyService service) {
this.mService = service;
}
}
③新建Myservice 继承 Service
public class MyService extends Service {
private IDataCallback mCallback;
private int mCount = 0;
public void setCallback(IDataCallback callback) {
MyService.this.mCallback = callback;
}
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(this);
}
Log.d(">>>>", "onCreate: 服务已启动");
sendData();
}
private void sendData() {
//模拟发送数据
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
@Override
public void run() {
if (mCallback != null) {
mCount++;
MyService.this.mCallback.dataCallback("发送数据次数:" + mCount);
}
if (mCount <= 10) {
handler.postDelayed(this, 1000);
}
}
};
handler.postDelayed(runnable, 1000);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(MyService.this);
}
public static void startForeground(Service ctx) {
try {
String CHANNEL_ONE_ID = "CHANNEL_ONE_ID";
String CHANNEL_ONE_NAME = "CHANNEL_ONE_ID";
int SERVICE_ID = 811;
NotificationChannel notificationChannel;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel(
CHANNEL_ONE_ID, CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(R.color.colorAccent);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager nm = (NotificationManager) ctx.getSystemService(NOTIFICATION_SERVICE);
if (nm != null) {
nm.createNotificationChannel(notificationChannel);
}
}
Intent intent = new Intent();
Class<?> className = Class.forName("com.struggle.servicedemo.MainActivity");
intent.setClassName(ctx, className.getName());
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx, CHANNEL_ONE_ID);
builder.setContentTitle("标题")
.setContentText("内容")
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MIN)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
Notification notification = builder.build();
ctx.startForeground(SERVICE_ID, notification);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
④Activity中代码
public class MainActivity extends AppCompatActivity implements IDataCallback {
private MyBinder mBinder;
private ServiceConnection mConn;
private Intent mIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startService(View view) {
startService();
}
public void bindService(View view) {
mConn = new MyServiceConn();
bindService(mIntent, mConn, Context.BIND_AUTO_CREATE);
}
private void startService() {
mIntent = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//ps 8.0 后一定要通过这种方式启动
startForegroundService(mIntent);
} else {
startService(mIntent);
}
}
@Override
public void dataCallback(Object obj) {
Log.d(">>>>>", "dataCallback: "+obj.toString());
}
private class MyServiceConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(">>>>", "onServiceConnected: 服务已绑定");
mBinder = (MyBinder) service;
mBinder.getService().setCallback(MainActivity.this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(">>>>", "onServiceDisconnected: 服务绑定失败");
mBinder = null;
unbindService(mConn);
}
}
}
至此通信已经完成,但是需要注意几点问题:
①清单中一定要注册服务(基本常识),不然服务怎么也启不了
<application
<service android:name=".MyService" />
</application>
②申请权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
不然会报:
Caused by: java.lang.SecurityException: Permission Denial: startForeground from pid=30149, uid=10249 requires android.permission.FOREGROUND_SERVICE
总结
代码中关于服务的启动方式已做了注释,8.0应该是谷歌修复了服务的漏洞,startForeground()主要是操作通知栏,提醒用户,什么正在运行的东东,7.0之前是可以通过启动两个相同id,隐藏通知栏,8.0经过测试是不行的,另外NotificationChannel和NotificationCompat的CHANNEL_ID要保持一致。
网友评论