Service内自定义类与Activity的通信及数据交互问题咨询
Hey there! Let's break down how to handle communication between your MyClass (running inside a Service) and the Calls Activity, since you can't initialize MyClass from the Activity directly. We'll cover interface registration, data/object transfer, and even some alternative approaches that might simplify things.
1. Using Interfaces for Communication (Your Preferred Approach)
The key here is to let your Activity bind to the Service holding MyClass, then register your interface callback directly with MyClass via the Service. Here's a step-by-step implementation:
Step 1: Define the Callback Interface
First, create an interface that defines the communication methods between MyClass and the Activity:
public interface MyClassCallback { void onPermissionStatusChanged(boolean hasOverlayPermission); void onDataReceived(YourCustomObject data); // For object/data transfer }
Step 2: Update MyClass to Support Callback Registration
Modify MyClass to hold a reference to the callback and expose methods to register/unregister it:
public class MyClass { private MyClassCallback mCallback; private Context mContext; public MyClass(Context context) { this.mContext = context; checkOverlayPermission(); } // Register callback public void setCallback(MyClassCallback callback) { this.mCallback = callback; } // Unregister callback (critical to avoid memory leaks) public void removeCallback() { this.mCallback = null; } private void checkOverlayPermission() { boolean hasPermission = Settings.canDrawOverlays(mContext); // Notify callback if registered if (mCallback != null) { mCallback.onPermissionStatusChanged(hasPermission); } if (hasPermission) { drawOverlay(); // Your overlay drawing logic } else { // Start Calls Activity with NEW_TASK flag (required when starting from Service) Intent intent = new Intent(mContext, Calls.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } } // Example method to send data to the Activity public void sendDataToActivity(YourCustomObject data) { if (mCallback != null) { mCallback.onDataReceived(data); } } private void drawOverlay() { // Your existing overlay implementation } }
Step 3: Update Your Service to Expose MyClass
Let your Service hold the MyClass instance and provide a way for the Activity to access it via a binder:
public class YourBackgroundService extends Service { private MyClass mMyClassInstance; @Override public void onCreate() { super.onCreate(); mMyClassInstance = new MyClass(this); // If your Service needs to run persistently, consider making it a Foreground Service } // Binder to let Activity access the Service and MyClass private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { YourBackgroundService getService() { return YourBackgroundService.this; } } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } // Expose MyClass to the Activity public MyClass getMyClassInstance() { return mMyClassInstance; } }
Step 4: Register the Callback in Calls Activity
Bind to the Service, get the MyClass instance, and register your callback:
public class Calls extends AppCompatActivity implements MyClassCallback { private YourBackgroundService mBoundService; private boolean mIsServiceBound = false; private MyClass mMyClass; // Service connection to bind and get the Service instance private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { YourBackgroundService.LocalBinder binder = (YourBackgroundService.LocalBinder) service; mBoundService = binder.getService(); mIsServiceBound = true; // Get MyClass and register the callback mMyClass = mBoundService.getMyClassInstance(); mMyClass.setCallback(Calls.this); } @Override public void onServiceDisconnected(ComponentName name) { mIsServiceBound = false; // Unregister callback to avoid leaks if (mMyClass != null) { mMyClass.removeCallback(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calls); // Bind to the Service when Activity starts Intent serviceIntent = new Intent(this, YourBackgroundService.class); bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); // Unbind Service and clean up callback if (mIsServiceBound) { if (mMyClass != null) { mMyClass.removeCallback(); } unbindService(mServiceConnection); mIsServiceBound = false; } } // Implement callback methods @Override public void onPermissionStatusChanged(boolean hasOverlayPermission) { if (hasOverlayPermission) { // Permission granted, close the Activity if needed finish(); } } @Override public void onDataReceived(YourCustomObject data) { // Process the received data/object // Note: This runs on the thread MyClass called it from, use runOnUiThread if updating UI runOnUiThread(() -> { // Update your Activity UI here }); } // Example method to send data from Activity to MyClass public void sendDataToMyClass(YourCustomObject data) { if (mIsServiceBound && mMyClass != null) { mMyClass.receiveDataFromActivity(data); // Add this method to MyClass } } }
2. Transferring Objects/Data
For passing custom objects between MyClass and the Activity, use Parcelable (recommended for Android) or Serializable:
Example Parcelable Object
public class YourCustomObject implements Parcelable { private String title; private int id; // Constructor, getters, setters // Parcelable implementation protected YourCustomObject(Parcel in) { title = in.readString(); id = in.readInt(); } public static final Creator<YourCustomObject> CREATOR = new Creator<YourCustomObject>() { @Override public YourCustomObject createFromParcel(Parcel in) { return new YourCustomObject(in); } @Override public YourCustomObject[] newArray(int size) { return new YourCustomObject[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(title); dest.writeInt(id); } }
3. Alternative: Using LiveData (Simpler for Lifecycle-Aware Communication)
If you want to avoid manual callback registration/unregistration, use LiveData (part of Jetpack). It's lifecycle-aware, so it automatically stops observing when the Activity is destroyed:
Update MyClass with LiveData
public class MyClass { private MutableLiveData<Boolean> mPermissionLiveData = new MutableLiveData<>(); private MutableLiveData<YourCustomObject> mDataLiveData = new MutableLiveData<>(); private Context mContext; public MyClass(Context context) { this.mContext = context; checkOverlayPermission(); } // Expose LiveData for Activity to observe public LiveData<Boolean> getPermissionLiveData() { return mPermissionLiveData; } public LiveData<YourCustomObject> getDataLiveData() { return mDataLiveData; } private void checkOverlayPermission() { boolean hasPermission = Settings.canDrawOverlays(mContext); mPermissionLiveData.postValue(hasPermission); // Post to main thread if (hasPermission) { drawOverlay(); } else { Intent intent = new Intent(mContext, Calls.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } } // Send data to Activity via LiveData public void sendData(YourCustomObject data) { mDataLiveData.postValue(data); } }
Observe LiveData in Calls Activity
public class Calls extends AppCompatActivity { private MyClass mMyClass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calls); // Bind to Service and get MyClass instance (same as before) // ... if (mMyClass != null) { // Observe permission status mMyClass.getPermissionLiveData().observe(this, hasPermission -> { if (hasPermission) finish(); }); // Observe data updates (automatically runs on main thread) mMyClass.getDataLiveData().observe(this, data -> { // Process data and update UI directly }); } } }
Key Notes
- Always add
Intent.FLAG_ACTIVITY_NEW_TASKwhen starting an Activity from a Service. - Clean up callbacks/unbind services in
onDestroy()to prevent memory leaks. - For persistent Service runs, consider using a Foreground Service with a notification to avoid system termination.
内容的提问来源于stack exchange,提问作者smsm smsm

