Catch exception - java

I got Activity, in onCreate() I try to fetch data:
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
...............
fillUI();
}
public void getDetailedItem(){
FeedParser parser=new FeedParser();
try{
mItem=parser.parseDetailed();
}catch(Exception e){
closeAndShowError();
}
}
public void fillUI(){
getDetailedItem();
if(mItem!=null){
...............
}else{
closeAndShowError();
}
}
private void closeAndShowError(){
Context context = getApplicationContext();
CharSequence text = getResources().getString(R.string.toast_error);
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
finish();
}
Them problem is, when Exception occures, it is caught, and I expect my activity to finish itself, but I still get code inside fillUI() executed, so I had to put if statement after getDetailedItem();
How do I solve it?

You can do something like this:
public void getDetailedItem(){
FeedParser parser=new FeedParser();
try{
mItem=parser.parseDetailed();
}catch(Exception e){
mItem = null;
}
}
Then in fillUI() if mItem is null you show's error;

You could pass the exception that might get thrown in parseDetailed to the fillUI method:
public void getDetailedItem() throws Exception {
FeedParser parser=new FeedParser();
mItem=parser.parseDetailed();
}
public void fillUI(){
try {
getDetailedItem();
// rest of your code..
} catch(Exception e) {
closeAndShowError();
}
}

The finish() statement is not an immediate return, your code continues to run until it decides it's completed. You need to leave yourself an indicator of the exception, and return up through your call chain when it happens, instead of continuing to run.

Since you are catching the Exception; the fillUI() function is never notified of any error.
Your codepath is as following:
fillUI -> getDetailedItem -> exception caught -> closeAndShowError -> finish() -> return to closeAndShowError -> return to getDetailedItem after caught block -> return back to fillUI.
So basically, the code after getDetailedItem in fillUI is called because you caught the exception and didn't let the other function know. Either by return value, or by throwing (and maybe catching) a new exception.

finish() does cause the Activity to stop as the next step in its lifecycle. But at the point you call it, it's still in the process of another lifecycle step: onCreate(). It will not exit, somehow, immediately in the middle of a method.
That's the answer, but, better would be to redesign your code a bit. This is probably not the cleanest way to handle it if you're dealing with questions like this.

Related

Getting error when using LogEventListener

I am getting an error when using a LogEventListener.
When I just print something, for example in the beforeEvent method, everything is fine, but when I set any action in any method, I get this error:
java.lang.StackOverflowError
at java.base/java.lang.String.replace(String.java:2173)
at com.codeborne.selenide.impl.SelenideElementDescriber.selector(SelenideElementDescriber.java:67)
at com.codeborne.selenide.impl.ElementFinder.elementCriteria(ElementFinder.java:137)
at com.codeborne.selenide.impl.ElementFinder.getSearchCriteria(ElementFinder.java:130)
at com.codeborne.selenide.impl.Alias$NoneAlias.getOrElse(Alias.java:43)
at com.codeborne.selenide.impl.WebElementSource.description(WebElementSource.java:60)
at com.codeborne.selenide.impl.SelenideElementProxy.invoke(SelenideElementProxy.java:81)
at jdk.proxy2/jdk.proxy2.$Proxy17.is(Unknown Source)
at com.bme.listeners.EventLogger.beforeEvent(EventLogger.java:16)
at com.codeborne.selenide.logevents.SelenideLogger.beginStep(SelenideLogger.java:121)
at com.codeborne.selenide.logevents.SelenideLogger.beginStep(SelenideLogger.java:57)
at com.codeborne.selenide.impl.SelenideElementProxy.invoke(SelenideElementProxy.java:81)**
Code:
public class EventLogger implements LogEventListener {
#Override
public void beforeEvent(LogEvent log) {
if(Selenide.$("path").is(visible)) {
System.out.println("Before Event");
}
}
#Override
public void afterEvent(LogEvent log) {
}
}
Can anyone help me understand?
My best guess as to the cause of this problem is that calling Selenide.$("path").is(visible) causes a log event to be generated. This would cause your listener gets called again, so Selenide.$("path").is(visible) gets called again, which causes another log event, so your listener gets called again, and so on and so on until the stack overflows. However, I don't have enough of your stacktrace to be sure. I would be more sure of this problem if you could include the stacktrace down as far as the second line in which com.bme.listeners.EventLogger.beforeEvent appears.
What might help is adding a boolean field to your listener that records whether it is currently logging an event, and does nothing if beforeEvent is called and this field is true:
public class EventLogger implements LogEventListener {
private boolean isCurrentlyLoggingAnEvent = false;
#Override
public void beforeEvent(LogEvent log) {
if (isCurrentlyLoggingAnEvent) {
// Prevent recursive call.
return;
}
isCurrentlyLoggingAnEvent = true;
try {
if(Selenide.$("path").is(visible)) {
System.out.println("Before Event");
}
}
finally {
isCurrentlyLoggingAnEvent = false;
}
}
#Override
public void afterEvent(LogEvent log) {
}
}
I've used a try-finally block to ensure that the field isCurrentlyLoggingAnEvent is always set back to false, even when an exception is thrown from within the try block.
If you plan to put any logic in afterEvent, you may want to consider doing something similar there.

How to Access Activity from ActivityScenarioRule

I am using ActivityScenarioRule for Espresso UI Testing and I wanted to get access to the method getStringArray(), calling which requires the Activity . So, is there any way to retrieve the Activity by the ActivityScenarioRule , maybe something similar to getActivity in ActivityTestRule.
#Rule
public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
I am not using ActivityTestRule, because it is deprecated!
Since it appears you're using Java, here's how you'd do it:
#Rule
ActivityScenarioRule<MainActivity> activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
#Test
public void test() {
activityScenarioRule.getScenario().onActivity(activity -> {
// use 'activity'.
});
}
Please read the documentation for more info on these new ways of interacting with the activity under test.
For anyone who wants Activity, but that without need to re-write all tests to run on UI-thread, a fairly straightforward Java way to get it:
Waiting for UI
Assume you want to test if a dialog is shown after some delay, the onActivity(...) hook runs on UI-thread, which means waiting in there would cause the dialog to be nerver shown.
In such cases you need to keep a strong-reference to ActivityScenario (as that prevents Activity close).
Test should wait for onActivity(...) hook to be called, then keep passed Activity's reference.
Finally, move test logic out of onActivity(...) hook.
Example
private ActivityScenario mActivityScenario;
#After
public void tearDown() throws Exception {
if (mActivityScenario != null) {
mActivityScenario.close();
}
mActivityScenario = null;
}
#Override
public Activity getActivity() {
if (mActivityScenario == null) {
mActivityScenario = ActivityScenario.launch(getActivityClassForScenario());
}
return tryAcquireScenarioActivity(mActivityScenario);
}
protected static Activity tryAcquireScenarioActivity(ActivityScenario activityScenario) {
Semaphore activityResource = new Semaphore(0);
Activity[] scenarioActivity = new Activity[1];
activityScenario.onActivity(activity -> {
scenarioActivity[0] = activity;
activityResource.release();
});
try {
activityResource.tryAcquire(15000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail("Failed to acquire activity scenario semaphore");
}
Assert.assertNotNull("Scenario Activity should be non-null", scenarioActivity[0]);
return scenarioActivity[0];
}
Espresso states the following:
At the same time, the framework prevents direct access to activities
and views of the application because holding on to these objects and
operating on them off the UI thread is a major source of test
flakiness.
When there is no other way I use the following method to get an arbitrary activity from an ActivityScenarioRule. It uses onActivity mentioned in the accepted answer:
private <T extends Activity> T getActivity(ActivityScenarioRule<T> activityScenarioRule) {
AtomicReference<T> activityRef = new AtomicReference<>();
activityScenarioRule.getScenario().onActivity(activityRef::set);
return activityRef.get();
}
Any onView(...) code inside onActivity led to a timeout in my testcases. So, I extracted the activity and used it with success outside the onActivity. Beware tho! See the statement above.
#Test
fun checkForUpdate() {
val scenario = ActivityScenario.launch(MainActivity::class.java)
scenario.onActivity {
UpdateTool.checkForUpdate(it)
}
}

How to make sure that NullPointerException doesn't come while showing infinite data from api.

I am fetching data from api and showing them into recyclerview.
I am using InfiniteScroll to load more data on scroll.
It works fine if I scroll smoothly but if I scroll fast I get NullPointerException as data is not being fetched on time even after I am checking if model data is not null in Adapter class.
How to avoid this situation?
Please find the code below:
#NonNull
private InfiniteScrollListener createInfiniteScrollListener() {
return new InfiniteScrollListener(10, linearLayoutManager) {
#Override public void onScrolledToEnd(final int firstVisibleItemPosition) {
offset += 10;
final List<Purchase2> itemsLocal = loadMore(offset);
refreshView(recyclerView, new PurchaseMainTestAdapter(itemsLocal, R.layout.list_orders_layout, getApplicationContext(), emptyView), firstVisibleItemPosition);
}
};
}
private List<Purchase2> loadMore(int index){
progressBar.setVisibility(View.VISIBLE);
//add loading progress view
purchases.add(new Purchase2());
adapter.notifyItemInserted(purchases.size()-1);
OneViewApi apiService =
ApiClient.getClient().create(OneViewApi.class);
Call<Orders> call;
call = apiService.getPurchaseData(auth_token,index,10);
Log.d("Called url is:", call.request().url().toString());
call.enqueue(new Callback<Orders>() {
#Override
public void onResponse(Call<Orders> call, Response<Orders> response) {
if(response.isSuccessful()){
//remove loading view
purchases.remove(purchases.size()-1);
List<Purchase2> result = response.body().getPurchases();
if(result.size()>0){
//add loaded data
purchases.addAll(result);
}else{//result size 0 means there is no more data available at server
adapter.setMoreDataAvailable(false);
Toast.makeText(getApplicationContext(),"No More Data Available",Toast.LENGTH_LONG).show();
}
progressBar.setVisibility(View.GONE);
//should call the custom method adapter.notifyDataChanged here to get the correct loading status
}else{
Log.e("Item list"," Load More Response Error "+String.valueOf(response.code()));
}
}
#Override
public void onFailure(Call<Orders> call, Throwable t) {
Log.e("Item list"," Load More Response Error "+t.getMessage());
}
});
return purchases;
}
at first use try{} catch{} then trace your code via break point to fine where exception happened
but i guess exception occur in here :purchases.remove(purchases.size()-1); that your list is null and you are trying to remove an item.(in first time) or about adding and removing items.
but for detect showing load more or not you can add null to purchases list then handle it in adapter - it's too better
Got the fix for this null pointer Exception:
1.) Added adapter code inside
try{} catch{} block
2.) Has set the flag so that on scroll it could not call the service again until last one is executed
if(!isLoading) {
isLoading = true;
final List<Purchase2> itemsLocal = loadMorePurchaseData(offset, null, null, null);
refreshView(recyclerView, new PurchaseMainAdapter(itemsLocal, R.layout.list_orders_layout, getApplicationContext(), emptyView), firstVisibleItemPosition);}
And in network call set the appropriate flag:
public void onResponse(Call<Orders> call, Response<Orders> response) {
if(response.isSuccessful()){
List<Purchase2> result = response.body().getPurchases();
if(result.size()>0){
//add loaded data
purchases.addAll(result);
}else{//result size 0 means there is no more data available at server
purchase_adapter.setMoreDataAvailable(false);
//telling adapter to stop calling load more as no more server data available
Toast.makeText(getApplicationContext(),"No More Data Available",Toast.LENGTH_LONG).show();
}
progressBar.setVisibility(View.GONE);
isLoading = false;
//should call the custom method adapter.notifyDataChanged here to get the correct loading status
}else{
Log.e("Item list"," Load More Response Error "+String.valueOf(response.code()));
}
Thanks everyone for giving the hint.

How to remove final variable used in a thread in Android

I'm using Retrofit2 and DBFlow. I have a problem when I save my data into my database. Certain data are duplicated when I'm calling the thread twice at the same time. The problem is my List because this variable is final. And I have to set final because I need to use this List in my thread.
Then, there is a way to remove the final to my List and replace by something ?
Retrofit onResponse()
public void onResponse(Call<AdminPictures> call, Response<AdminPictures> response) {
AdminPictures apResponse = response.body();
// Here is my list
final List<PictureInfos> pictureInfos = apResponse.getPicturesList();
new Thread(new Runnable() {
#Override
public void run() {
try {
// The List is used here
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()){
infos.save();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
move the declaration of the list to the top of the class and make a class variable...
List<PictureInfos> pictureInfos....
Make it a class variable.
Otherwise, instead of anonymous class; move your code into a separate class. Then pass the variable using constructor
Please try this
synchronized(this) {
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()){
infos.save();
}
}}
What I'm trying, is to first let insert a thread data, meanwhile block another.
Maybe you have request twice and response twice,so thread is excuted twice.
Maybe this is silly, but have you tried clearing the variable at the end of the for cycle?
...
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()) {
infos.save();
}
}
pictureInfos.clear();
...

Exception while writing to MifareUltralight

I get an exception while trying to write a MifareUltralight NFC tag on Android. The error shown in logcat is:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setVisibility(int)' on a null object reference at
.MainActivity.getTagInfo(MainActivity.java:124)
com.mynfctest.MainActivity.resolveIntent(MainActivity.java:106)
com.mynfctest.MainActivity.onNewIntent(MainActivity.java:98)
How I can solve this error?
Write method:
public static boolean writeOnMifareUltralight(Context _context, Tag tag, String pageData, int i) {
MifareUltralight mifare = null;
int size=pageData.length();
try {
mifare = MifareUltralight.get(tag);
mifare.connect();
mifare.writePage(i, pageData.getBytes(Charset.forName("US-ASCII")));
} catch (Exception ex) {
ex.printStackTrace();
Log.d("skm", ex.getMessage());
// return false;
} finally {
try {
mifare.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return true;
}
Write method called from my activity:
switch (mifareUlTag.getType()) {
case MifareUltralight.TYPE_ULTRALIGHT:
boolean result=NFCHammer.writeOnMifareUltralight(this,tag,tvName.getText().toString(),4);
if(result){
findViewById(R.id.incProgressBar).setVisibility(View.GONE);
Intent Callintent = new Intent(this, HomeActivity.class);
Callintent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(Callintent);
finish();
overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
}
else{
findViewById(R.id.incProgressBar).setVisibility(View.GONE);
CommonTask.createToast("Tap The card again!!!", this, Color.RED);
}
break;
}
As the exception explains, you are calling the method setVisibility on a null ob ject reference:
java.lang.NullPointerException: Attempt to invoke virtual method '... setVisibility(int)' on a null object reference
In other words, somewhere in your code, you have a like this:
object.setVisibility(...);
and on this like the variable object is null and does not reference a real object.
The stacktrace of the exception further tells you that the exception occured on line 124 of the file MainActivity.java, more specifically inside a method named getTagInfo:
at .MainActivity.getTagInfo(MainActivity.java:124)
As you did not reveal enough code to verify if the problem comes from those code snippets that you posted, we can only speculate that the problem comes from the two lines that set the visibility of a view R.id.incProgressBar to GONE:
findViewById(R.id.incProgressBar).setVisibility(View.GONE);
If that's the case, then findViewById(R.id.incProgressBar) returned null, indicating that the view R.id.incProgressBar was not found in the current view hiearchy of the activity.

Categories

Resources