Android MVP, same old context problem when getting clipboard data? - android

So I need to get clipboard data in presenter. This means I need context. Unfortunately I don't have any knowledge of dependency injection if it is the only standard way.
I studied some solutions but they are stated as faulty solutions.
public class MainActivityPresenter implements MainActivityInterfaces.MainToPresenter {
MainActivityInterfaces.PresenterToMain main;
public MainActivityPresenter (MainActivityInterfaces.PresenterToMain main) {
this.main = main;
}
#Override
public void processUrl() {
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
}
And this is the method in Utils class
public static String getClipboardData (Context context) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager cm = (android.text.ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
String result = cm.getText().toString();
return result==null?"":result;
} else {
ClipboardManager cm = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
if (item != null)
return item.getText().toString();
else
return "";
}
}

The right way, The presenter shouldn't know about any specific Android element, ClipboardManager or Context (ie: it should be pure Java). The clipboard logic should be taking place within the View and returning whatever it needs to the Presenter.
The ugly way, if you want to continue with this design, processUrl should take Context, presumably this method is being called from the view? So that is where context can be provided.
#Override
public void processUrl(Context context) {
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
In the view:
presenter.processUrl(getApplicationContext());
Another solution is to ask for context from the presenter to the view:
#Override
public void processUrl() {
final Context context = view.getContext();
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
//In the view
void Context getContext() {
return getApplicationContext();
}

At MVP, when the View propagates an event to the Presenter, it is very common for certain information to be sent to the Presenter. In your case, it would be quite logical to rename your method as follows:
#Override
public void processUrl(String url) {
if (url.isEmpty()) {
} else {
}
}
In this way, the View will be in charge of propagating the information along with the event. And you also keep in the Presenter the ignorance about the peculiarities of the View (Android things), something very important in the MVP pattern.
Good luck!

I know this won't help your question. But aside from MVP, MVVM is much robust and cleaner pattern that doesn't require as much boilerplate code as MVP does, so if you are still in a phase where you are starting a project and deciding or an architecture, I can, as a professional Android developer, only recommend MVVM. MVVM also does the state saves due to LiveData living inside ViewModels so you don't have to painfully add the ability to the presenter via saved instance bundles.
If you need DI in order to inject the context into the presenter (which isn't desirable, context shouldn't get into presenters ideally but sometimes it happens as it is harder to create entities that do the context required job in the presenter), and you are new and unexperienced in DI, then with Java you need to go with Dagger 2, which has a steep learning curve. Switching to Kotlin and then using Koin would be perhaps easier than learning Dagger 2, but if you are into it, try it. In any way it is a required to have DI in a professional projects.
If you don't want to use DI in order to pass the context into the presenter, then pass it into the constructor. I guess the Activity or Fragment is where you initialise your presenter (creating a new instance of it) and where you pass the 'view' concept into the presenter. If you need context in the presenter (again, not the best thing to do but ok, you are a new into patterns I understand), then just pass it into the constructor and set it in the presenter.

Using context in Presenter is not a good idea.
What if I need the Context?
Well, get rid of it. In cases like this, you should ask yourself why you need the context. You may need the context to access shared preferences or resources, for example. But you shouldn’t do that in the presenter: you should access to resources in the view and to preferences in the model. These are just two simple examples, but I can bet that the most of the times it is just a problem of wrong responsibilities.
You can pass url in constructor from View.
public class MainActivityPresenter implements MainActivityInterfaces.MainToPresenter {
MainActivityInterfaces.PresenterToMain main;
private String mUrl;
public MainActivityPresenter (MainActivityInterfaces.PresenterToMain main, String mUrl) {
this.main = main;
this.mUrl = mUrl;
}
#Override
public void processUrl() {
if (mUrl.isEmpty()) {
} else {
}
}
interface SomeView {
void onUrlProcessed();
}
}
In your MainActivity:
class MainActivity {
String url = Utils.getClipboardData(getContext());
}
You can refer other answers here .
Does the presenter having knowledge of the Activity / Context a bad idea in the MVP pattern?
Android MVP: safe use Context in Presenter

Related

Android - MVP using Activity/Context

I've been dabbling with MVP for android and have read from different sites about not passing an activity as argument into the presenter as it's anti-pattern but i have come across a problem where i need to use a third party method that requires the activity/context.
I've thought of using Dependency Injection, doing another abstraction or just pass it only to the method that requires it, rather than the constructor but i'm confused with what's better.
An example in Kotlin (Presenter)
fun Food(*should activity be passed here?*) {
var bar = Foo(activity).build
Stand(bar, callback{})
}
Would be great if someone can enlighten me on this.
I don't think any obvious reason why you should avoid passing Activity Context to Presenter. I heard that you should make presenter independent of any Android packages because you can reuse them for Desktop/TV or other platforms, but I don't think this will work.
If you need activity in your presenter you have several ways to do it
(my DI cases will use Dagger2 library but you can take a look at another ones):
Just pass activity in setter/constructor, to make it safer use WeakReference. This is how we do it in our project.
public class BasePresenter implements IPresenter {
protected VIEW view;
private WeakReference<FragmentActivity> activityWeakReference = new WeakReference<>(null);
#Override
public void onStart(FragmentActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
#Override
public void onStop() {
activityWeakReference.clear();
}
#Override
public void onViewCreated(Bundle savedInstanceState) {
}
public Optional<FragmentActivity> getActivity() {
return Optional.ofNullable(activityWeakReference.get());
}
public void getActivity(Action1<FragmentActivity> action) {
getActivity().ifPresent(action::call);
}
#Override
public void setView(VIEW view) {
this.view = view;
}
#Override
public boolean onBackPressed() {
return false;
}
#Override
public void navigationButtonClick() {
getActivity(Activity::finish);
}
#Override
public boolean onCustomActivityResult(int requestCode, int resultCode, Intent data) {
return false;
}}
You can provide your context to dagger modules like this.
You can use Dagger Android Injector. Personally I'm not a big fan of it but this should work. Read this article.
Do not forget to null your activity when presenter not needed anymore, because they are huge in memory so it will cause leaks.
If you want to work in a "clean way" you should always push the frameworks (android, third party libraries, data sources...) to the outer layer of your architecture (as far as possible from your business logic).
In your case I would create a "wrapper class" for the 3rd party library and the context and I would inject the interface of this class as a constructor parameter of your presenter.
In this way your presenter is clean and is not affected if you have to change the 3rd party library.
Here you can read more about clean architecture:
https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
https://github.com/android10/Android-CleanArchitecture
https://android.jlelse.eu/a-complete-idiots-guide-to-clean-architecture-2422f428946f

Android MVP: which layer should store context variable

I find myself where i need to play a sound file when user clicks a button on a view.
MediaPlayer requires a context to be created.
What is the best way to put MediaPlayer initialization code?
Should I pass a context into a presenter method and play it there?
Or is it ok to just play on the view.
Context is a part of Android View Layer in MVP, so Presenter must not have any idea about it and you should not pass it to presenter.
You have to add a methods to your View interface and implement it inside your android view components (i.e. Activity or Fragment) and use them to do an action in View layer as playing a sound.
Presenter must ask for the UI event and View must handle it!
Here is a MVP sample using Dagger, RxJava and Retrofit, which might help you to learn more about MVP in Android:
https://github.com/mmirhoseini/marvel
I often put business logic code in Model Layer (don't make confusion with model in database). I often rename as XManager for avoiding confusion (such as ProductManager, MediaManager ...) so presenter class just uses for keeping workflow.
The rule of thumb is no or at least limit import android package in presenter class. This best practice supports you easier in testing presenter class because presenter now is just a plain java class, so we don't need android framework for testing those things.
For example here is my mvp workflow.
View class: This is a place you store all your view such as button, textview ... and you set all listeners for those view components on this layer. Also on this View, you define a Listener class for presenter implements later. Your view components will call methods on this listener class.
class ViewImpl implements View {
Button playButton;
ViewListener listener;
public ViewImpl(ViewListener listener) {
// find all view
this.listener = listener;
playButton.setOnClickListener(new View.OnClickListener() {
listener.playSong();
});
}
public interface ViewListener {
playSong();
}
}
Presenter class: This is where you store view and model inside for calling later. Also presenter class will implement ViewListener interface has defined above. Main point of presenter is control logic workflow.
class PresenterImpl extends Presenter implements ViewListener {
private View view;
private MediaManager mediaManager;
public PresenterImpl(View, MediaManager manager) {
this.view = view;
this.manager = manager;
}
#Override
public void playSong() {
mediaManager.playMedia();
}
}
Manager class: Here is the core business logic code. Maybe one presenter will have many managers (depend on how complicate the view is). Often we get Context class through some injection framework such as Dagger.
Class MediaManagerImpl extends MediaManager {
// using Dagger for injection context if you want
#Inject
private Context context;
private MediaPlayer mediaPlayer;
// dagger solution
public MediaPlayerManagerImpl() {
this.mediaPlayer = new MediaPlayer(context);
}
// no dagger solution
public MediaPlayerManagerImpl(Context context) {
this.context = context;
this.mediaPlayer = new MediaPlayer(context);
}
public void playMedia() {
mediaPlayer.play();
}
public void stopMedia() {
mediaPlayer.stop();
}
}
Finally: Put those thing together in Activities, Fragments ... Here is the place you initialize view, manager and assign all to presenter.
public class MyActivity extends Activity {
Presenter presenter;
#Override
public void onCreate() {
super.onCreate();
IView view = new ViewImpl();
MediaManager manager = new MediaManagerImpl(this.getApplicationContext());
// or this. if you use Dagger
MediaManager manager = new MediaManagerImpl();
presenter = new PresenterImpl(view, manager);
}
#Override
public void onStop() {
super.onStop();
presenter.onStop();
}
}
You see that each presenter, model, view is wrapped by one interface. Those components will called through interface. This design will make your code more robust and easier for modifying later.
This is such a long post for answering your question. I think this is suitable because everyone has their own MVP implementation (core flow is same, but minorities are different). So I present here a workflow I often use in work. Hoping you see this useful :)
If you need a generic Context you can extend Application, declare a static context variable and after you can get this Context into your presenter.

How do I abstract away dependencies in Android library code?

Here is my scenario.
I have an android activity in which I want to abstract my I/O dependencies. The dependencies are represented by this interface (edited for brevity and simplicity):
public interface ITimeDataServer {
TimeRecord[] get(int userID);
void save(TimeRecord record);
}
What I want is for my activity to be able to call these interface methods, and leave the implementation to be supplied by the calling code. (Pretty standard, I think).
ITimeDataServer myServer;
int myUserID;
void loadRecords() {
TimeRecord[] records = myServer.get(myUserID);
// etc...
}
My difficulty is, how can I ensure that myServer gets set?
This seems like a common problem, but I can't find a clean solution.
My first thought would be that myServer would be passed in through the constructor, but Android activities aren't really instantiated with constructors.
I've come up with several solutions, but they're all icky in some way:
Icky Solution 1
Create a static method to launch the activity class which takes an ITimeDataServer parameter and stores it in a static variable from which the activity can access it:
private static ITimeDataSource theDataSource;
public static void launch(Activity currentActivity, ITimeDataSource dataSource) {
theDataSource = dataSource;
Intent intent = new Intent(currentActivity, MainActivity.class);
currentActivity.startActivity(intent);
}
This is icky because (a) the data source is static and not actually associated with the instance, and (b) a consumer could initiate the activity by the standard activity API rather than this static method, which will cause NullPointerException.
Icky Solution 2
I can create a Provider class which provides a singleton instance of ITimeDataSource, which needs to be initialized by the calling library before use:
public class TimeDataSourceProvider {
private static ITimeDataSource myDataSource = null;
public void initialize(ITimeDataSource dataSource) {
myDataSource = dataSource;
}
public ITimeDataSource get() {
if (myDataSource == null)
throw new NullPointerException("TimeDataSourceProvider.initialize() must be called before .get() can be used.");
else
return myDataSource;
}
}
This seems a little less icky, but it's still a little icky because the activity's dependency is not obvious, and since there may be many paths to launch it, it's highly possible that some of them would forget to call TimeDataSourceProvider.initialize().
Icky solution 3
As a variation on #2, create a static IODependencyProvider class which must be initialized with ALL dependencies on app startup.
public class IODependencyProvider {
static ITimeDataSource myTimeData;
static IScheduleDataSource myScheduleData; // etc
public static void initialize(ITimeDataSource timeData, IScheduleDataSource scheduleData /* etc */) {
myTimeData = timeData;
myScheduleData = scheduleData;
//etc
}
public static ITimeDataSource getTimeData() {
if (myTimeData == null)
throw new NullPointerException("IODependencyProvider.initialize() must be called before the getX() methods can be used.");
else
return myTimeData;
}
// getScheduleData(), etc
}
This seems superior to #1 and #2 since a failure to initialize would be much harder to sneak by, but it also creates interdependencies among the data types that otherwise need not exist.
...and other icky variations on that theme.
The common themes that make these solutions crappy:
the need to use static fields to pass non-serializable information to an activity
the lack of ability to enforce initialization of those static fields (and subsequent haphazardness)
inability to clearly identify an activity's dependencies (due to reliance on statics)
What's a nooby Android developer to do?
As long as these dependencies implement Parcelable correctly, you should be able to add them to your intent, then unparcel them as ITimeDataServer and get the correct class.
I found a nice solution here, in the least-loved answer.
I define the library activity as abstract and with no default constructor, but a constructor that takes an interface, like so:
public abstract class TimeActivity extends AppCompatActivity {
private ITimeDataSource myTimeDataSource;
public TimeActivity(#NonNull ITimeDataSource dataSource) {
myTimeDataSource = dataSource;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time);
// do stuff with myTimeDataSource!
}
}
Then, the calling code can create a concrete subclass with its chosen implementation that does have a parameterless constructor. No static members, easy-peasy!
This allows you to abstract and inject all sorts of crazy behaviours! Woooo!
(Note that the concrete subclass activity needs to be manually added to AndroidManifest.xml, like all activities, or the app will crash when it tries to launch.)

Design solutions for fragments and asynchronous processing in Android

I struggled with some issues about design complex tasks with fragments
use. Fragments and asynchronous approach are quite new for me, so I think it will be better to describe my app.
Application
App works with GitHub API and has two screens: list of repositories and details about selected one. For retrieving data from json I use Retrofit and store it in SQLite. Little remark As I understood Retrofit can be used asynchronously but in case of additional work with DB it is better to use asynchronous approach for operations under DB also. In my case I'm checking internet connection: in case of absence I load data from DB. Otherwise I upgrade DB and then use it. Now I want to add fragments for different screen density support( usual master - detail workflow).
And my questions are
Where is the better place to run async tasks? Is it a right solution to make it in activity and then pass result to fragments?
What is the better solution for asynchronous processing? As I understood from my search about that, AsyncTask is deprecated but the easiest solution.
AsyncTask is a pain in the rear. Many beginners still seem to use it, but imho it's not worth learning it. Sooner or later you'll be bugged out by it because AsyncTask is error prone, has lots of caveats and tons of boilerplate code.
Retrofit does make it's calls asynchronously automatically, so you've got that covered already. Retrofit also plays very nice with RxJava which is I guess considered the way of doing asynchronous things on Android these days.
RxJava has a steeper learning curve initially than other patterns, but it's worth learning. If you got your database stuff working already, it won't be much work making ti asynchronous with Rx.
As for
Is it a right solution to make it in activity and then pass result to
fragments?
If you don't follow an MVP design approach, which is okay, in my opinion it's absolutely okay to do 'business logic' stuff in the Fragment and not let the Fragment call the Activity, then let the Activity get back to the Fragment. Whichever is easier for you and suits your app.
You can place your background thread anywhere, but make sure it is cancelled if the class is garbage collected, and don't keep references (Context, callbacks) in your thread.
public class MyActivity extends Activity {
private static interface OnDownloadThreadCompleteListener {
public void onDone(String data);
}
private static class DownloaderThread extends AsyncTask<Void, Void, String> {
private OnDownloadThreadCompleteListener mListener;
public DownloaderThread(OnDownloadThreadCompleteListener listener) {
mListener = listener;
}
#Override
protected String doInbackground(Void... args) {
// Do your network request here
return result;
}
#Override
public void onPostExecute(String data) {
if (mListener != null && !isCancelled()) {
mListener.onDone(data);
}
mListener = null;
}
#Override
public void onCancelled() {
mListener = null;
}
}
private DownloaderThread mThread;
private OnDownloadThreadCompleteListener mListener;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
mListener = new OnDownloadThreadCompleteListener() {
#Override
public void onDone(String data) {
Fragment fragment = getFragmentManager().findFragmentByTag("fragment_git");
fragment.show(data);
}
}
mThread = new DownloaderThread(mListener);
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mThread.execute(null, null, null);
}
}
}
#Override
public void onPause() {
super.onPause();
if (mThread != null) {
mThread.cancel(true);
}
mThread = null;
mListener = null
}
}

RxJava as event bus?

I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger

Categories

Resources