Is it fine to pass an activity instance to a class? - android

There is a scenario in which i need to pass activity and its button to a java class.
I did following and its working fine, I am only concerned if its the right way of integrating this.
MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button= findViewById(R.id.btn);
UIComponents uiComponents= new UIComponents();
uiComponents.setActivity(this, button);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d("result", String.valueOf(requestCode));
}
UIComponents.class
public class UIComponents {
public void setActivity(final AppCompatActivity activity, Button btn){
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent= new Intent(activity, ResultActivity.class);
activity.startActivityForResult(intent, 999);
}
});
}
This works perfectly fine, It displays toast message on my activity screen, also i am able to receive onActivityResult callback on my activity. I am concerned if this can lead to any performance related issues.

you can just pass only the Button and use the getContext method available e.g
public class UIComponents {
public void setActivity(Button btn){
Context activity = btn.getContext();
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent= new Intent(activity, ResultActivity.class);
activity.startActivityForResult(intent, 999);
}
});
}

The most important thing to consider here is the lifecycle of your components. In general you shouldn't pass a reference to an activity to any object which might outlive it. In theory (probably very rear), your activity might be killed between the time the user clicks the button and the time the OnClickListener (if it wasn't garbage collected yet) will be executed and you'll get a NullPointerException. Also, you might pass (even accidentally) UIComponents to some other object with a different lifecycle.
Best practice in such a case is usually using MVP architecture. The specific case of handling button clicks is described here among other places.
In addition, following the "Guide to App Architecture" by Google is probably a good idea.

Related

getSerializableExtra() is null randomly

There is a link in one activity, after I click on it it opens another activity. I add an enum parameter for the second activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
binding.myLink.setOnClickListener(new View.OnClickListener() {
// ...
#Override
public void onClick(View view) {
Intent intent = new Intent(getActivityContext(), SecondActivity.class);
intent.putExtra(KEY, MyEnum.ENUM_VALUE);
startActivity(intent);
}
});
// ...
}
In onCreate of second activity I read this parameter
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
// get value
FirstActivity.MyEnum value = (FirstActivity.MyEnum ) getIntent()
.getSerializableExtra(AboutActivity.KEY);
// ...
}
Everything works but in Crashlytics I see that for some users value is null. Second activity is called only from first one and from nowhere else.
Can someone suggest me the scenario with this behavior? When can it happen like this?
I opened my app, opened the second activity and put app to background. After several hours I opened my app from the list of apps and everything is ok. No more ideas when it can happen.
use this :
#Override
public void onClick(View view) {
Intent intent = new Intent(getActivityContext(), SecondActivity.class);
Bundle bundle=new Bundle();
bundle.putSerializable(serializableKEY,yourSerializableClass);
intent.putExtras(KEY, bundle);
startActivity(intent);
}
});
and in second activity :
Bundle bundle=getIntent().getExtras();
yourSerializableClass clazz=(yourSerializableClass)bundle.getSerializable(serializableKEY);

Intent GridLayout to 4 Activities

I have GridLayout with a CardView inside. I want to create an Intent to 4 different activities. I only can execute one Intent. I do not know if I should use Else If or case. Thanks for your help. Here is my code.
GridLayout mainGrid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dashboard);
mainGrid = (GridLayout)findViewById(R.id.grid);
setSingleEvent(mainGrid);
}
private void setSingleEvent(GridLayout mainGrid) {
for (int i=0;i<mainGrid.getChildCount();1++)
{
CardView cardView = (CardView)mainGrid.getChildAt(i);
final int final1= i;
cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i=new Intent(DashboardActivity.this,MapsActivity.class);
startActivity(i);
}
});
}
}
I think your problem comes from a lack of understanding how the order of execution of the program works. If you insert a cycle for and then you put inside an anonymous class as it is the case of View.onViewClickListener is normal that you will have only one result, because you break the for cycle.
The way to go should be to not use a for cycle, but assigning different Explicit Intents depending by what you want to obtain.
EDIT. On the base of your use case you need to trigger the Intent from the Adapter. Please see here and mainly here, basically you need to use the Android SDK functionality that tells you which card has been clicked mRecyclerView.getChildLayoutPosition(view); then depending from your needs you may (or not) pass a switch case for educational purposes, although possibly is not the most elegant and efficient way to solve.
If you want a different Activity to be started depending on what CardView is clicked try something like the following:
private void setSingleEvent(GridLayout mainGrid) {
mainGrid.getChildAt(0).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(i);
}
});
mainGrid.getChildAt(1).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(i);
}
});
//do this for all 4 cardViews.
}
In short: set all Intents separately.

pause an activity meanwhile another one is running

I would like to get voice recognition using just one method.
In order to do that i've created 3 classes
the main class
public class Index extends Activity {
private Button boton;
private EditText texto;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
boton = (Button)findViewById(R.id.boton);
texto = (EditText) findViewById(R.id.texto);
boton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
texto.setText(IVRecognition.getInstancia().getComando(Index.this));
}
});
}
}
the intermediate
public class IVRecognition {
//*******************singleton********************
private static IVRecognition instancia;
private IVRecognition (){}
public static IVRecognition getInstancia(){
if (instancia==null) instancia = new IVRecognition();
return instancia;
}
//************************************************
public static String resultado = null;
public String getComando(Context content){
Intent intent = new Intent(content, VRecognition.class);
content.startActivity(intent);
//pause here untill VRecognition.onActivityResult is executed
return resultado;
}
}
and the recognition one
public class VRecognition extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startRecognition();
}
public void startRecognition (){
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
startActivityForResult(intent, 1 /*VOICE_RECOGNITION_REQUEST_CODE*/);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1 /*VOICE_RECOGNITION_REQUEST_CODE*/ && resultCode == RESULT_OK){
ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
IVRecognition.getInstancia().resultado = result.get(0);
}
this.finish();
}
}
The problem is that when I call VRecognition activity using content.startActivity(intent); the execution of the aplication keeps on going, so the variable called resultado has null value until onActivityResult is executed, which results in a null return value.
Hope you can help me. Cheers
Ian's answer is good. But from your comment, I'd recommend using an IntentService and the BroadcastManager. That way you don't need the intermediate activity. You call the startService(intent) from any activity that wants the VR result (and implements BroadcastReceiver). Then the IntentService calls startActivityForResult(intent,1) and Broadcasts the result.
More info:
http://developer.android.com/training/run-background-service/index.html
It sounds like you want to pause execution until voice recognition is complete. You may want to rethink this; you're calling getComando() from your UI thread, so your application UI will be frozen until recognition is complete. In the (probably quite likely) event that recognition takes more than five seconds, the system will pop up an Application Not Responding dialog. (Also, since you're implementing getComando() by starting another activity within your process, blocking the UI thread in getComando() would prevent recognition from ever running.)
The right way to do this is to use a completion callback. For instance, you could create an IVRecognitionListener interface:
public interface IVRecognitionListener {
public void onRecognitionComplete(String resultado);
}
and pass an instance of that to getComando(). Then instead of just setting IVRecognition.resultado in onActivityResult(), you could call onRecognitionComplete() to notify the caller of the result.

Android: execute method after returning to main activity from intent activity

I have seen many examples with
startActivityForResult(Intent, int)
and then using
onActivityResult(int, int, Intent)
but for me I dont need to pass anything, I simply want to startActivity(intent), and when intent activity returns, a method get called in main activity..
Any tutorial on doing this?
EDIT:
Here a sample code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_options);
createEvent = (Button) findViewById(R.id.createEvent);
createEvent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(optionsInterface.this, MainActivity.class);
startActivity(intent);
}
});
}
here, after I return from MainActivity (press back, or just close it), I want the activity to perform a task ( with no data being passed from MainActivity)
you can use startactivityforResult().. it doesn't matter if you send any data back or not... when you use startactivityonResult() the method onActivityResult will get called...just check for the request code there and do whatever you want...sending back something is not necessary.
Instead of startActivity() you might want to use startActivityForResult(), so that you get a call back on result.

A problem with image capture on Motorola Milestone

Below is the simple code fragment to illustrate the problem.
Why the value of the field "tag" logged in method "onActivityResult" not being "tag_modified"?
I also tried others async call of "startActivityForResult", but no such problem exists.
The problem merely occurs on my Moto Milestone, but everything goes well on HTC G7.
public class HelloSnapshot extends Activity {
private static Logger logger = Logger.getLogger(HelloSnapshot.class.getName());
final int REQUESTCODE_SNAPSHOT = 1;
String tag = "tag_initial";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = new Button(this);
button.setText("BUTTON");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tag = "tag_modified";
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUESTCODE_SNAPSHOT);
}
});
setContentView(button, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
public void onActivityResult(int requestCode, int resultCode , Intent data) {
switch (requestCode) {
case REQUESTCODE_SNAPSHOT:
if (resultCode == Activity.RESULT_OK) {
logger.info(tag);
}
break;
}
}
}
I`ve found it out...
Some android OS kill the snapshot calling Activity to avoid memory related exception.
So, I have to save all the states via method onSaveInstanceState, and retrieve them when the calling activity was constructed again.
Further more, I also found out that, all the information stored in the memory is prone to be erased, like those Singleton objects. Thus I have to do saving by some persistent storage approaches, and restore them later.

Categories

Resources