Keeping it modular in android - Dividing resources in a good way
Background
I'm trying to keep an app which is as modular as possible.
The app will have have tasks which it performs on different intervals. My goal is to make it as easy possible to add new tasks with minimal understanding of the underlying architecture and without having to modify other files but at the same time not over complicating the code.It would be perfect if all you needed to do to add a new task is to create the file and that's it.
This will require loading the tasks in runtime which I don't really like, I could live with a single place where all the registration is done (this also enabled toggling of the task)Right now I've have an abstract task class which has a piece of static code which registers all tasks (basically adds them to a list).
Problem
Each task will have their own set of preferences and possibly resources.
Dividing strings and array is pretty easy by using prefixes for the names but the main issue comes with the preferences.Right now I'm using a PreferenceActivity
to display my preferences.
PreferenceScreen
. All tasks have only one thing common and that is an "Enabled" checkbox.
I don't want to store all the preferences in one file as it has the possibility to get quite messy that way.
Current solution
Right now each task have a method setupPreferences(PreferenceScreen)
in which they can add whatever options they want. This however has the downside of being programmatically which is not all that bad but I'd like to avoid that if possible.
Desired solution
The optimal solution would be if each task could have their own XML file which is loaded and added to the root PreferenceScreen
, as far as I know however this is not possible, the only way to load it is into a PreferenceActivity
.
Other notes
If anyone has any other suggestions on dividing resources in android feel free to share them :)
Thanks
NicklasClarification
The tasks I'm talking about are never 开发者_高级运维third party, they will internal only. This is more of a way to early on get a good structure of this app.
By using reflection I'm calling PreferenceManager.inflateFromResource(Context, int, PreferenceScreen)
to create a PreferenceScreen
from my XML files.
String resources are separated in separate files and prefixed with taskname_
Here is the code for inflating the PreferenceScreen
, it should be placed in a PreferenceActivity
:
/**
* Inflates a {@link android.preference.PreferenceScreen PreferenceScreen} from the specified
* resource.<br>
* <br>
* The resource should come from {@code R.xml}
*
* @param resId The ID of the XML file
* @return The preference screen or null on failure.
*/
private PreferenceScreen inflatePreferenceScreenFromResource(int resId) {
try {
Class<PreferenceManager> cls = PreferenceManager.class;
Method method = cls.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
return (PreferenceScreen) method.invoke(getPreferenceManager(), this, resId, null);
} catch(Exception e) {
Log.w(LOG_TAG, "Could not inflate preference screen from XML", e);
}
return null;
}
Here is an example of how to use it:
package com.example;
import java.lang.reflect.Method;
import com.example.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.util.Log;
public class ExamplePreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "ExamplePreferences";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Sets the preference name
PreferenceManager pm = getPreferenceManager();
pm.setSharedPreferencesName(PREFERENCE_NAME);
// Adds default values and the root preference screen
PreferenceManager.setDefaultValues(this, PREFERENCE_NAME, MODE_PRIVATE, R.xml.preferences_layout, false);
addPreferencesFromResource(R.xml.preferences_layout);
PreferenceScreen root = getPreferenceScreen();
// Includes R.xml.other_preferences_layout and adds it to the bottom of the root preference screen
PreferenceScreen otherPreferenceScreen = inflatePreferenceScreenFromResource(R.xml.other_preferences_layout);
root.addPreference(otherPreferenceScreen);
PreferenceManager.setDefaultValues(this, PREFERENCE_NAME, MODE_PRIVATE, R.xml.other_preferences_layout, false);
}
/**
* Inflates a {@link android.preference.PreferenceScreen PreferenceScreen} from the specified
* resource.<br>
* <br>
* The resource should come from {@code R.xml}
*
* @param resId The ID of the XML file
* @return The preference screen or null on failure.
*/
private PreferenceScreen inflatePreferenceScreenFromResource(int resId) {
try {
Class<PreferenceManager> cls = PreferenceManager.class;
Method method = cls.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
return (PreferenceScreen) method.invoke(getPreferenceManager(), this, resId, null);
} catch(Exception e) {
Log.w(LOG_TAG, "Could not inflate preference screen from XML", e);
}
return null;
}
}
This example would use res/xml/preferences_layout.xml
as the root and then add res/xml/other_preferences_layout.xml
to the bottom of the root.
Not exactly an answer to your question, but might be interesting anyway: have a look at plugin API for Locale and Tasker: http://www.twofortyfouram.com/developer.html
Locale and Tasker are phone automation apps. Both are highly configurable and modular, they accept third party plugins to extend their functionality. Similar to your case, each plugin has unique preferences. Their solution to the problem is, each plugin brings its own preferences activity, accessible using specific intent action. There are UI guidelines so that preferences screens look consistent.
精彩评论