Service和Activity通信总结

一点感想

说感想也算是工作中的一些总结吧,做项目基本都会使用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要保持一致。

猜你喜欢

转载自blog.csdn.net/zhuhuitao_struggle/article/details/104808689