I am working on a News app using newsapi. I have a homepage that downloads the top 15 articles and another page that has does the same but from the hackernews api. Each time I run the app from Android Studio, it shows a white screen for a few minutes before populating the list view.
I tried limiting the download rate, kept a check on the SQLite indexes and other things from StackOverflow but I can't seem to solve my issue.
When I go to the hackernews page and click on list, the app will just refresh itself and come back to the homepage
When running app from the emulator, it will either go to hackernews without loading homepage or just crash
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1 when clicking last item in listview
App will only load 2 articles and give me a java.io.FileNotFoundException: https://www.summarizebot.com/api/summariz... error. This is because the URL from the API doesn't work (tested in the browser)
Homepage
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
...
//START THE BACKGROUND TASK
task = new BackgroundTask();
String s = task.execute("https://newsapi.org/v2/top-headlines?country=us&apiKey").get();
...
//POPULATE LIST WITH DATABASE CONTENT
updateContent();
public void updateContent(){
...
if(cursor.moveToFirst()){
homeStories.clear();
homeLinks.clear();
}
if(cursor.getCount() > 0) {
do {
homeStories.add(cursor.getString(nameIndex));
homeLinks.add(cursor.getString(addressIndex));
} while (cursor.moveToNext());
adapter.notifyDataSetChanged();
}
//BACKGROUND TASK
public class BackgroundTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground (String...urls){
//GET JSON AND SUMMARIES IN SINGLE FOR LOOP FOR EACH ARTICLE
...
for (int i = 0; i < 16; i++) {
JSONObject content = jsonArray.getJSONObject(i);
...
//ADD TO DATABASE
database.execSQL("INSERT INTO trending (name, address) VALUES ('" + title + "','" + address + "')");
//GET THE SUMMARY OF EACH ARTICLE
url = new URL("https://www.summarizebot.com/api/summarize?...
JSONArray j2Array = new JSONArray(s1);
for (int j = 0; j < j2Array.length(); j++) {
JSONObject object2 = j2Array.getJSONObject(j);
s2 += object2.getString("sentence");
}
//END OF MAIN FOR LOOP
}
Hackernews Page
//SAME AS ABOVE
After attempting to troubleshoot, I came up with a few questions:
How can I speed up background tasks for news articles?
How can I open an app and start background downloads without it crashing because of indexOutOfBounds?
How can I give download priority to the current activity instead of downloading everything at once?
How do I skip over broken links and continue getting the rest of the articles? App stops downloading at that point [FIXED: Surrounded InputStream with Try/Catch]
Related
I am working on an application where I am connecting with the BLE device and sending commands.
One of that commands we have a command for changing the Bluetooth device name.
Communication is working fine, but the problem is when we send the command for changing the name it was working, BLE confirms the input and sends us the output, but when we disconnect and run LE Scan it was showing the same name as the previous, it should show the new name of the device.
If I want to get the latest name of the device I need to open the Bluetooth page manually in the device and scan over there in the scan result it was showing the latest name, when I open the app again which is in the background and its scanning under LE scan function with 10-sec delay, it was showing the new name in the list.
How can I ask Bluetooth manager or system to refresh the cache or refresh data for that Bluetooth device ?.
I don't know it was right to create ticket, but i have created ticket in google issue tracker : https://issuetracker.google.com/issues/233924346
Thanks.
I had the same problem and solved it by reading the new name from the raw scan data. In this way you never have to use device.getName() which returns the old name from the cache. This is Android Java code for the scan callback function.
private ScanCallback newscancallback()
{
ScanCallback scb;
// Device scan callback.
scb = new ScanCallback()
{
#Override
public void onScanResult(int callbackType, ScanResult result)
{
super.onScanResult(callbackType, result);
int n,k,len,getout;
BluetoothDevice dev;
byte[] rec;
StringBuilder nameb;
String name;
dev = result.getDevice();
// do not use dev.getName() which returns cached name
// read current name from raw scan record instead
name = null;
rec = result.getScanRecord().getBytes();
len = rec.length;
nameb = new StringBuilder();
n = 0;
getout = 0;
// search scan record for name
while(n < len-2 && rec[n] != 0 && getout == 0)
{
// rec[n] is length of next item
// rec[n+1] is item type - look for 8 or 9=name
// rec[n+2].. is the name, length rec[n]-1
if(rec[n] > 1 && (rec[n+1] == 8 || rec[n+1] == 9)
{ // found name
for(k = 0 ; k < rec[n]-1 ; ++k)
nameb.append((char)rec[n+2+k]);
name = nameb.toString();
getout = 1;
}
else // go to next item
n += rec[n] + 1;
}
// name is now null or the new name from the scan record
}
#Override
public void onScanFailed(int errcode)
{
}
#Override
public void onBatchScanResults(List<ScanResult> result)
{
}
};
return (scb);
}
As you can see the latest name in the Bluetooth settings of the mobile device, I believe there is no issue with the Bluetooth manager of the system. The issue will be in the scanning function of the code as it is not actually refreshing the scan list yet and it might saved the last known BLE list somewhere in the cache. If you are using third-party library, you might need to check their documentation or codes about how the scan function actually works. There may be like force-refresh option or something in the library. As far as I know, to save the device's battery, there is a delay to actually refresh the scan list.
Info:
I am doing an application where multiple Users need to work on one project. There are a few tasks that need to be completed.
Problem:
When a user turns off his screen, and turns it on around 5 minutes later (Or he went to the Homescreen or any other app for around 5 min ), firebase takes up to a Minute to get the new Data into the App (I have offline storing off btw). It frustrates the Users, because the Data needs to be up to date all the time so they can work with updated Tasks.
EDIT: THIS IS HAPPENING EVERYWHERE IN ALL IMPLEMENTATIONS LIKE THE EXAMPLE BELOW
Question:
Why is it like this?
How can I solve it???
EDIT: Example:
final FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference doc = db.collection("user").document(email).collection("dates").document(date);
doc.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
HashMap CodeMap = (HashMap) document.getData();
Set keySet = CodeMap.keySet();
Object[] key = keySet.toArray();
for (int i = 0; i < keySet.size(); i++) {
if (!key[i].equals("clockIn") && !key[i].equals("clockOut") && !key[i].equals("completeTime") && !key[i].equals("clockPauseFrom") && !key[i].equals("clockPauseTo")) {
userWorkList.add(new User_Work_View_Row(key[i].toString(), email, date, i));
System.out.println(key[i] + " FOUND");
}
}
}else{
Log.d(TAG, "get failed with ", task.getException());
getActivity().finish();
}
}
}
});
SOLUTION:* (Kind of)
I have Implemented the solution of the answer below and did some Testing with the Battery options.
I found out that disabling Battery Optimizations and Background restrictions(if its even there) works! The Data is coming right away. I don't think its a good solution but it works for my App.
What you're observing is likely a result of an exponential backoff in the frequency of connection retries performed by the Firestore SDK. This backoff saves much battery compared to always retrying immediately in a tight loop.
If you want to force a retry, then you could try manually calling disableNetwork() followed by enableNetwork() to get it to retry immediately.
I am creating a music player app in android. In the MainActivity I'm listing all the (.mp3,.wav,.flac) files in a simple listview. I'm using this getSongs method to search for such files:
public void getSongs(File root) {
File listFile[] = root.listFiles();
if (listFile != null && listFile.length > 0) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory() && !listFile[i].isHidden()) {
getSongs(listFile[i]);
} else {
if (listFile[i].getName().endsWith(".mp3") ||
listFile[i].getName().endsWith(".MP3") ||
listFile[i].getName().endsWith(".flac")||
listFile[i].getName().endsWith(".wav")) {
songList.add(listFile[i]);
}
}
}
}
}
Then from the main activity I'm calling this:
File externalSD = new File("/storage/"); //gets the external but not internal!!
getSongs(externalSD);
File internalSD = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); //gets the internal but not external!!
getSongs(internalSD);
Works well for me (I have only 100 songs) but in case you have a large number of such files it is really slow (my friend has around 2.5k songs..).
I would like to know how I can achieve this more efficienty.
You could try:
Using an AsyncTask to retrieve the songs in another thread.
Getting the song list from Android's media database instead - you will have to work with SQL and MediaStore.Audio. Remember to use an AsyncTask or a Loader.
The second option would be faster since you don't have to traverse the whole system.
I am reading UDP packets and i wanna display that info on UI as table in android app.
Here is my code,
try {
byte buffer[] = new byte[10000];<br/>
InetAddress address = InetAddress.getByName("192.168.xx.xx");<br/>
int port = xxx;<br/>
Log.d("..........","What will Happen ?? ");<br/>
for(int k=0;k<50;k++) { // 50 rows are added , This i wanna make it 5000+ rows so it takes plenty of time to load that table <br/>
DatagramPacket p = new DatagramPacket(buffer, buffer.length, address, port);<br/>
DatagramSocket ds = new DatagramSocket(port);<br/>
Log.d("..........","Perfect Binding .... Waiting for Data");<br/>
ds.receive(p);<br/>
Log.d("..........","Packet Received");<br/>
byte[] data = p.getData();<br/>
String result = "";<br/>
int b[] = new int[data.length];</br>
for (int i=0; i < 150; i++) {<br/>
result += Integer.toString( ( data[i] & 0xff ) + 0x100, 16).substring( 1 );<br/>
result += "_";<br/>
}<br/>
Log.d("Result => ",result); <br/>
TableLayout tl=(TableLayout)findViewById(R.id.TableLayout01);<br/>
TableRow tr=new TableRow(this);
TextView tv= new TextView(this);
TextView tv2 = new TextView(this);
tv.setPadding(5, 0, 5, 0);
tv2.setPadding(5,0,5,0);
String k1 = Integer.toString(k);
tv.setText(k1);
tv2.setText(it_version);
tr.addView(tv);
tr.addView(tv2);
tl.addView(tr,1);
ds.close();
}
} catch (Exception e) {
Log.e("UDP", "Client error", e);
}
If i keep 50 rows am able to display it properly without any time delay, if i put 3000 rows its taking too long time and sometimes app is hanging... I wanna add 50 entries to a table and load the table and again read 50 entries and append to the table without touching any button or anything so i have a table in UI and it will update automatically by reading UDP packets ... how i can achieve that ?? Any clue appreciated.
or once i read the UDP packet i wanna display it on UI[appending to the table],How i can do this ??[Scrolling and all i will take care] please let me know
I already tried using threads but no use
Basically, you need to implement an infinite listview. There are a couple strategies to do this:
You can get all the data and store it in a database and only show the user 50 at a time.
You can fetch only 50 at first and then fetch the next 50 when the user scrolls past them.
You can fetch 100, show 50 and then show next 50 when the user scrolls past the first 50. Pre-fetch the next 100 to show next and so on.
Once you figured out your fetching strategy, you need to implement the actual adapter and listview. Here's a good technique to do this. I would recommend that you don't re-invent the wheel and use this great library called EndlessAdapter unless you want to implement it for learning purposes.
Something like this is what you might use in order to get a infinite list effect when you don't have a cursor.
Please note this is a very rough draft since I deleted the code only relevant to my app, to help for you clarity, and for my privacy and the apps privacy. Also it may not be the best way of doing everything, but it worked the first time I wrote it (which took like 10 minutes) and worked beautifully for a very complex list, so I haven't bothered coming back to it.
class AsyncGetUpdates extends AsyncTask<Void, Void, List<UpdateDTO>>
{
#Override
protected void onPreExecute()
{
showDialog();
super.onPreExecute();
}
#Override
protected List<UpdateDTO> doInBackground(Void... params)
{
return APIHelper.getUpdates(count);
}
#Override
protected void onPostExecute(List<UpdateDTO> result)
{
killDialog();
isCurrentlyUpdating = false;
setAdapterData(result);
super.onPostExecute(result);
}
}
public void setAdapterData(List<UpdateDTO> result)
{
killDialog();
if (this != null && this.getActivity() != null)
{
Log.d(TAG, "setAdapterData");
if (lvUpdatesList.getAdapter() != null)
{
// save index and top position
int index = lvUpdatesList.getFirstVisiblePosition();
View v = lvUpdatesList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
updateListAdapter = new UpdateListAdapter(this.getActivity().getLayoutInflater(), result, this);
lvUpdatesList.setAdapter(updateListAdapter);
lvUpdatesList.refreshDrawableState();
lvUpdatesList.setSelectionFromTop(index, top);
}
else
{
updateListAdapter = new UpdateListAdapter(this.getActivity().getLayoutInflater(), result, this);
lvUpdatesList.setAdapter(updateListAdapter);
lvUpdatesList.refreshDrawableState();
}
}
// add in a listener to know when we get to the bottom
lvUpdatesList.setOnScrollListener(new OnScrollListener()
{
#Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// we do not want to update if we already are
if (isCurrentlyUpdating == false)
{
if (lvUpdatesList.getAdapter() != null && lvUpdatesList.getAdapter().getCount() == count)
{
final int lastItem = firstVisibleItem + visibleItemCount;
if (lastItem == totalItemCount)
{
isCurrentlyUpdating = true;
// add to the count of views we want loaded
count += 20;
// start a update task
new AsyncGetUpdates().execute();
}
}
}
}
});
}
Finally I would like to say that copy pasting might get you the results you want, but it will hinder you future ability. I would say study, read, learn, try, fail, and try again.
Using AlarmManager I can set alarm for any time from the android App. But is there any way to list all the alarms set by me. What should be my approach towards that, as AlarmManager do not provide with such methods. Should I go for saving the alarm as a file in the memory.?
Please Help me with this.
Visiting all the possible links I came to a solution that creating an app that would retrieve the alarms set on the OS level is not possible. Ya.. to some extent it would be possible but in many cases it would be machine dependent.
So better option is to save your Alarms in your database.
A. There is a good explanation on this post regarding this. There are no guarantees that the AlarmClock app will be on every device your app is installed on. For example, many of the HTC phones replace it with HTC's own "World Clock" app.
However, assuming the stock AlarmClock app is present, you should be able to get a cursor from its content provider. See this project as an example.
B. You have to create a layout for items of the ListView.
You can find tutorials about this on Internet : http://www.vogella.com/articles/AndroidListView/article.html http://codehenge.net/blog/2011/05/customizing-android-listview-item-layout/
c.
final String tag_alarm = "tag_alarm";
Uri uri = Uri.parse("content://com.android.alarmclock/alarm")
Cursor c = getContentResolver().query(uri, null, null, null, null);
Log.i(tag_alarm, "no of records are" + c.getCount());
Log.i(tag_alarm, "no of columns are" + c.getColumnCount());
if (c != null) {
String names[] = c.getColumnNames();
for (String temp : names) {
System.out.println(temp);
}
if (c.moveToFirst()) {
do {
for (int j = 0; j < c.getColumnCount(); j++) {
Log.i(tag_alarm, c.getColumnName(j) + " which has value " + c.getString(j));
}
} while (c.moveToNext());
}
}