AtomicReferences for Intents and Activity Navigation in Helper File - android

In my app, I navigate between about 5 different screens, each in its own activity. Pretty much any activity can be called from any other activity, so I am trying to build a helper file to manage the intents so that I don’t have redundant code.
I built a helper file with public static methods and pass the activity context and any required data when calling these methods. This appears to be working fine on my device (Samsung Galaxy S5), but Android Studio recommends making my intents AtomicReference in my helper file.
Can you help me understand if and why these should be AtomicReference<Intent>?
Also, is it appropriate to pass context to a helper file to make these calls?
ActivityHelper file:
public class ActivityHelper {
private ActivityHelper() {}
public static void startAddNewMealActivity(Context context) {
Intent newMealIntent = new Intent(context, MealEditActivity.class);
context.startActivity(newMealIntent);
}
public static void startMealListActivity(Context context) {
Intent intent = new Intent(context, MealListActivity.class);
context.startActivity(intent);
}
public static void startEditMealActivity(Context context, FBMeal meal, String mealFBKey) {
Intent intent = new Intent(context, MealEditActivity.class);
intent.putExtra(Constants.INTENT_FB_KEY_EXTRA_TAG, mealFBKey);
intent.putExtra(Constants.INTENT_MEAL_EXTRA_TAG, meal);
context.startActivity(intent);
}
public static void startEditLastMealActivity(final Context context) {
FBHelper.getQueryForMostRecentMeal().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (FBHelper.isExistingDataSnapshop(dataSnapshot)) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
FBMeal selectedMeal = snapshot.getValue(FBMeal.class);
String selectedMealId = snapshot.getKey();
startEditMealActivity(context, selectedMeal, selectedMealId);
}
} else {
Utils.showToastFromStringResource(R.string.no_meals, context);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Utils.showToastFromStringResource(R.string.error_getting_meal, context);
}
});
}
}
Example of calling helper file from menu in AppCompatActivity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.edit_meal_menu:
ActivityHelper.startEditMealActivity(this, meal, mealFBKey);
return true;
case R.id.edit_last_entry_menu:
ActivityHelper.startEditLastMealActivity(this);
return true;
case R.id.about_menu:
DialogFragment newFragment = AboutDialog.newInstance();
newFragment.show(getFragmentManager(), "about");
default:
return super.onOptionsItemSelected(item);
}
}

I cannot see any reason at all why you would need to use an AtomicReference in any of your static methods.
Another approach would be to create a BaseActivity class that extends AppCompatActivity and includes all of your helper methods. All of your activities should then extend BaseActivity. In that case, you would not need to pass a Context to all of these helper methods, since the helper methods would be non-static and can just use this as Context.

Related

What is Design Pattern where we can pass the string name to a method and that method chooses which further method to call

I recently got following example where we are passing the action name to the method as string and then the method decides the function that needs to be called.
is this a good way of solving problem or is there some better way as well
public static final String ACTION_CHARGING_REMINDER = "charging-reminder";
public static void executeTask(Context context, String action) {
if (ACTION_INCREMENT_WATER_COUNT.equals(action)) {
incrementWaterCount(context);
} else if (ACTION_DISMISS_NOTIFICATION.equals(action)) {
NotificationUtils.clearAllNotifications(context);
} else if(ACTION_CHARGING_REMINDER.equals(action)){
issueChargeReminder(context);
}
}
I'd do something like this. This can be extended as much as you want, and obviously just an example:
static abstract class ActionHandler {
private String action;
public ActionHandler(String action) {
this.action = action;
}
public boolean canHandleAction(String input) {
return this.action.equals(input);
}
public abstract void handleAction();
}
static class OneActionHandler extends ActionHandler {
public OneActionHandler(String action) {
super(action);
}
#Override
public void handleAction() {
//...
}
}
static class TwoActionHandler extends ActionHandler {
public TwoActionHandler(String action) {
super(action);
}
#Override
public void handleAction() {
//...
}
}
static class Test {
private ActionHandler[] handlers;
public Test() {
handlers = new ActionHandler[]{new OneActionHandler("action1"), new TwoActionHandler("action2")};
}
public void handleAction(String action) {
for(ActionHandler i : handlers) {
if(i.canHandleAction(action)) {
i.handleAction();
break;
}
}
}
}
This sounds a lot like the react/redux, action/reduction pattern.
Reducers specify how the application's state changes in response to
actions sent to the store. Remember that actions only describe what
happened, but don't describe how the application's state changes.

Need Context in Model in MVP

I need to use the Context of activity in the model while using MVP in android to get the list of all the installed application.what is the correct way to access the context or any alternative to achieve the same while following the MVP pattern.
Here are the classes:
Main Activity.java
public class MainActivity extends BaseActivity
implements MainView,View.OnClickListener {
private MainPresenter mPresenter;
private Button sendButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
createPresenter();
}
private void init(){
sendButton= (Button) findViewById(R.id.button_send);
sendButton.setOnClickListener(this);
}
private void createPresenter() {
mPresenter=new MainPresenter();
mPresenter.addView(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.button_send:
mPresenter.onSendButtonClick();
break;
}
}
#Override
public void openOptionsActivity() {
Intent intent=new Intent(this,OptionsActivity.class);
startActivity(intent);
}
}
Main Presenter.java
public class MainPresenter extends BasePresenter {
MainModel model;
public void onSendButtonClick() {
model.getListOfAllApps();
}
#Override
public void addView(MainView view) {
super.addView(view);
model = new MainModel();
}
}
Main Model.java
public class MainModel {
public void getListOfAllApps(){
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0);
}
}
Having issue in getPackageManager().queryIntentActivities(mainIntent, 0) .how to do it as not having any context here.
I answered a similar question here which you may want to have a look at too. I'll give the breakdown of how I think you could solve this particular problem though.
Use a static context from the Application class
This method would work but I'm not fond of it. It makes testing harder and couples your code together.
public class App extends Application {
private static Context context;
public static Context getContext() {
return context;
}
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
Then in your MainModel:
public class MainModel {
public List<String> getListOfAllApps(){
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = App.getContext().getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = new ArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
}
Now we've got that out the way, let's look at some better options.
Do it in the Activity
So your Activity implements your View. It's probably doing a few Anrdoidy things too such as onActivityResult. There's an argument for keeping Android code in the Activity and just accessing it through the View interface:
public interface MainView {
List<String> getListOfAllApps();
}
The Activity:
public class MainActivity extends BaseActivity implements MainView {
//..
#Override
public List<String> getListOfAllApps(){
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = new ArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
//..
}
And the Presenter:
public class MainPresenter extends BasePresenter {
public void onSendButtonClick(){
view.getListOfAllApps();
}
}
Abstract the details in a separate class
Whilst the last option doesn't break the rules of MVP it doesn't feel quite right as getting a list of packages isn't really a View operation. My preferred option is to hide the use of Context behind an interface/class.
Create a class PackageModel (or whatever name takes your fancy):
public class PackageModel {
private Context context;
public PackageModel(Context context) {
this.context = context;
}
public List<String> getListOfAllApps(){
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = new ArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
}
Now have your Presenter require this as a constructor parameter:
public class MainPresenter extends BasePresenter {
private PackageModel packageModel;
public MainPresenter(PackageModel packageModel) {
this.packageModel = packageModel;
}
public void onSendButtonClick(){
packageModel.getListOfAllApps();
}
}
Finally in your Activity:
public class MainActivity extends BaseActivity implements MainView {
private MainPresenter presenter;
private void createPresenter() {
PackageModel packageModel = new PackageModel(this);
presenter = new MainPresenter(packageModel);
presenter.addView(this);
}
}
Now the use of Context is hidden from the Presenter and it can carry on without any knowledge of Android. This is known as constructor injection. If you're using a dependency injection framework it can build all the dependencies for you.
If you wanted to you could make an interface for PackageModel but I don't think it's really necessary as a mocking framework like Mockito can create a stub without using an interface.
Basically, you have the following options:
1) Always pass a Context to the Model. Whatever event happens in Android, you always have some kind of Context available. (And your code is invoked only in response to events.)
2) getApplicationContext() and store it for future use in a static variable.
There are the following gotchas:
An Activity is a Context, but if you store a link to an Activity, you get a memory leak. Activities are re-created when for example the screen turns.
The same about contexts passed to BroadcastReceivers and other kinds of Context. All of them have a lifetime, and that lifetime is not what you need for Model.
It is possible that your application is killed and restarted by Android. In this case, some global (static) variables may be set to null. That is, they will be null unless your application happens to write something to them. In particular, a static variable pointing to the application context may become null in one of restart scenarios. Such problems are difficult to test against.

Multiple Interfaces in Single Fragment

I have a Fragment that needs to communicate more than one Action back to it's Activity. For example,
When a button is clicked, it needs to communicate the onClick back to the Activity.
2.When a user's login and password match, a boolean value is sent to the Activity notifying it to start an Intent.
My first question is, is this common where a Fragment needs to relay more that one type of Action back to the Activity? And secondly, how is this solved? Is the following a good way to do it...
I created a custom class, which extends Fragment and included the two interfaces that I need (One to pass the onClick back to the Activity and One to pass a boolean value):
public class CustomInterfaceFragment extends Fragment {
public OnClickedListener listener;
public LogInInterface loggedInListener;
static interface OnClickedListener{
public void buttonClicked(View v);
}
static interface LogInInterface{
public void userLoggedIn(boolean loggedIn);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (OnClickedListener)activity;
this.loggedInListener = (LogInInterface)activity;
}}
I then extended this custom class in my Fragment and used the appropriate methods where needed. This is the onClick method in the Fragment...
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.register_button:{
listener.buttonClicked(v);//***Pass onClick Back to Activity
break;
}
case R.id.fragment_login_loginButton:{
ParseUser.logInInBackground(userName.getText().toString(), password.getText().toString(), new LogInCallback() {
#Override
public void done(ParseUser user, ParseException e) {
if (user!=null){
boolean verified = user.getBoolean("emailVerified");
if(!verified){
Toast.makeText(getActivity(),"Please Verify",Toast.LENGTH_LONG).show();
progressDialog.dismiss();
ParseUser.logOut();
}else{
progressDialog.dismiss();
loggedInListener.userLoggedIn(true);//***Pass boolean Back to Activity
}
}else {
Toast.makeText(getActivity(),e.getMessage(),Toast.LENGTH_LONG).show();
progressDialog.dismiss();
}
}
});
}
break;
}
}
Finally I implemented the custom fragment class and its interfaces in my Activity in order to retrieve the data.
Is this a reasonable way to solve this problem or am I missing something? The application seems to work fine. I just want to know what the best programming practice would be. Thank you.
all i can say is you can bring down this two interfaces to one like this below
public interface fragmentInteractions{
public void OnClickedListener(View v);
public void userLoggedIn(boolean loggedIn);
....
....
}
and i don't think the interface here needs to be static
Elaborating on Avinash Joshi's answer :
public interface CustomListener {
void onButtonClicked();
void onLoginResult( boolean isUserLoggedIn ); // You can pass User object via this method in case its required to do some operations
}
public class MainActivity extends Activity implements CustomListener {
#Override
public void onCreate( Bundle savedInstance ) {
// Initialize UI elements
// Initialize Fragment
}
#Override
public void onButtonClicked() {
//Action to be performed on button click
}
#Override
public void onLoginResult( boolean isUserLoggedIn ) {
if( isUserLoggedIn ) {
//take user to dashboard or any other screen
//Usually with the help of SupportFragmentManager
}
else {
//Take user to signup screen with an optional toast message
//In case parameters like User name and password need not be entered by user again, you can access them as function parameters and pass them to signupFragment via bundle
}
}
}
public class LoginFragment extends Fragment {
CustomListener mCustomListener;
#Override
public void onAttach( Context context ) {
super.onAttach( Context context );
try {
mCustomListner = (CustomListener) context;
} catch ( ClassCastException e {
Log.e(TAG, "Activity must implement CustomListener")
}
}
//Rest of Fragment initialization code here
}
Here's a complete example :
http://www.truiton.com/2015/12/android-activity-fragment-communication/

Download data from internet in background and concurrently share them among all activities. How?

I need to download elements from internet and add them to an arraylist in the background. (The download may take a few minutes.)
There is a loop in which part of overall elements are downloaded each iteration and added to the list. I need different activities be able to have access to that arraylist whenever needed, no matter if the download (the loop) is in progress or finished.
It seems a service can do this, but i don't have any idea on how. Considering the code below, how can i achieve this?
class A extends Service {
void foo(){
//uses a loop to get elements from internet
//then adds the elements to myArraylist in each loop
}
}
class B extends Activity {
//needs to have access to myArraylist asynchronously
}
class C extends Activity {
//needs to have access to myArraylist asynchronously
}
Note that i need the download process stay active when user switches between activities.
You can do it by Broadcast receiver.For send the data on other activity you can use:
intent = new Intent(ApplicationSetting.NEW_MESSAGE_ACTION);
intent.putExtra(IMMessage.IMMESSAGE_KEY, msg);
sendBroadcast(intent);
For receive this message for other any activity you can use this code:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
/*
* For before commit
*/
if (ApplicationSetting.NEW_MESSAGE_ACTION.equals(action)) {
IMMessage message = intent
.getParcelableExtra(IMMessage.IMMESSAGE_KEY);
Log.w("message", "are" + message);
}
}
};
So the problem you face with what you are asking is that your download loop may be adding to or changing the list while the active activity may also be accessing the same list. This can cause a ConcurrentModificationException. To avoid this what you need to do is synchronise all activity with the list. In order to make it available to all activities and have it accessible to your service I would suggest that the list itself is stored in your application (a class extending Application)
public class MyApplication extends Application {
private List<MyElement> mElems;
#Override
public void onCreate() {
super.onCreate();
mElems = Collections.synchronizedList(new ArrayList<MyElement>());
//this line will start your download service, available accross the whole app
startService(new Intent(getApplicationContext(), A.class));
}
//You can use accessor methods and keep the list private to ensure
//synchronisation doesn't get missed anywhere
public void synchronisedAddElement(MyElement elem) {
mElems.add(elem); //already synchronous in this case
}
//I havent tested this method, you method below may be safer
public Iterator getElementsIteratorSynchronised() {
synchronized(mElems) {
return list.iterator();
}
}
public Iterator iterateElementsSynchronised(OnElementListener lis) {
synchronized(mElems) {
Iterator<MyElement> i = list.iterator();
if (lis != null) {
while (l.hasNext()) {
lis.onElement(l.next());
}
}
}
}
public static class OnElementListener {
public void onElement(MyElement el);
}
}
You would write to it as follows
class A extends Service {
void foo(){
MyApplication app = (MyApplication) getApplication();
... //do your network call loop here, adding to local list
app.synchronisedAddElement( myNewElement );
}
}
And Read
class B extends Activity {
//the async task just because your comment said async access
new AsynTask<MyApplication, Void, Void>() {
public Void doInBackground(MyApplication app) {
app.iterateElementsSynchronised(new OnElementListener() {
public void onElement(MyElement el) {
Log.d(TAG, "Did somethign appropriate with " + el);
}
})
}
}.execute( (MyApplication) getApplication() );
}
Please just treat this as pseudo code, I've written it on the train home so the method signatures may vary, but this should get you where you need to be
Using the structure recommended by Nick Cardoso but with many changes to meet my case, i managed to solve the problem. here it is:
class A extends Service {
ArrayList arrayList = new ArrayList();
MyApplication app;
void foo(){
new Thread (new Runnable (){
#Override
public void run() {
app = (MyApplication)getApplication();
While(true){
//get elements from network and put them in arrayList
app.synchronisedAddCollection(arrayList);
LocalBroadcastManager.getInstance(this).sendBroadcast(mediaIntent);
}
}
}).start();
}
}
And here is my Application class:
public class MyApplication extends Application {
List<HashMap<String, String>> myArrayList = new ArrayList<HashMap<String, String>>();
#Override
public void onCreate() {
super.onCreate();
}
public void synchronisedAddCollection(ArrayList<HashMap<String, String>> arrayList) {
myArrayList.addAll(arrayList);
}
public ArrayList<HashMap<String, String>> getArrayList(){
return (ArrayList<HashMap<String, String>>) myArrayList;
}
}
Here is the activity which needs to access the shared arraylist
class B extends Activity {
MyApplication app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(getApplicationContext(), MyService.class);
LocalBroadcastManager.getInstance(this).registerReceiver(lbr,
new IntentFilter("mediaIntent"));
}
private BroadcastReceiver lbr = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
app = (MyApplication)getApplication();
//now i have access to the app arrayList
System.out.println(app.myArrayList.size());
}
}
};
}
Do not forget to register MyApplication and MyService in manifest.

Passing instances of classes between activitys

If I got class A and class B, class A act as a menu with 2 buttons one to connect one to login. When I press connect i run this method:
private void connect(){
Thread t1 = new Thread(){
public void run(){
connection_class = new ConnectionClass();
connection_class.run();
}
};t1.start();
}
which calls my ConnectionClass which does this in the constructor:
public ConnectionClass(){
socket = new Socket("address", port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
}
works great im connected to the server and press login which does (without the onClick stuff):
connection_class.MethodToWriteToServer("CommandThatLogsMeIn");
Intent i = new Intent().setClass(v.getContext(), Class.class);
startActivity(i);
This works fine but when im in Class B I want to use the same instance of it. I could just do a new thread and instance of the class but that would defeat the purpose of the start menu and require me to log in once more.
Is it somehow possible to pass the instance as a parameter to the activity when starting it or whats the android way of doing it?
As a sidenote I dont really NEED the menu but ive got some spare time before the assignment is due and thought I might aswell try it.
I have just finished a project like this yesterday.
For example you have this connection manager, called WebService:
// singleton class
public class WebService {
private static WebService instance;
private WebService() {}
public static WebService getInstance() {
if (instance == null)
instance = new WebService();
return instance;
}// getInstance()
public void login() {};
public void getFeeds() {};
public void logout() {};
}
Then you can put it in an base activity like this:
public class WebServiceActivity extends Activity {
private final WebService fWebService = WebService.getInstance();
protected WebService ws() { return fWebService; }
}
Then, you have two activities, LoginActivity and WorkingActivity:
public class LoginActivity extends WebServiceActivity {
buttonLogin.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
try {
ws().login();
// start WorkingActivity if logging in ok
} catch (...) { ... }
}
});
}
public class WorkingActivity extends WebServiceActivity {
buttonGetFeeds.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ws().getFeeds();
}
});
buttonLogout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ws().logout();
finish();
}
});
}
Anyway, there are many approaches. The one above is mine. Hope it helps you :-)
I am still don't sure if this is the correct way or not. But I prefer to use a static instance of the class like this:
// Create this class just once
public class MediaManager {
public static MediaManager instance;
public MediaManager() {
instance = this;
}
public void addNewImage(Bitmap bitmap) {
//....
}
}
//
public class AnotherClass {
public void doSomething() {
MediaManager.instance.addNewImage(..);
}
}
If somebody know a better way of using Manager Classes please make comment.

Categories

Resources