I want to fetch audio metadata periodically like song name and artist name.
for that i am using media metadata retriever in Async-task.
So problems are
1) In AsyncTask class how can i assign text to Taxt-view.
2) How can i call that Async-task class within a specific time, like 30sec time interval.
3) There are some text which i need to fetch only once in a day. so how can check that when last time data was fetched? and also which is the best method to store that fetch data and use it to Listview?(i mean i need to store that that into database or any hashmap or array list?)
Here is metadata retriver code
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.afragment, container, false);
}
#Override
public void onStart() {
super.onStart();
initfrag();
}
private void initfrag() {
// TODO Auto-generated method stub
lv1=(ListView)getView().findViewById(R.id.list);
tv=(TextView)getView().findViewById(R.id.tv1);
tv1=(TextView)getView().findViewById(R.id.tv2);
LongOperation task=new LongOperation();
task.execute();
}
class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
String link = "http://xyz-radio-link.ogg";
metaRetriever.setDataSource(link, new HashMap<String, String>());
// get mp3 info
title = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
artist = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
metaRetriever.release();
refreshtext();
return null;
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
public void refreshtext() {
// TODO Auto-generated method stub
Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"fonts/Roboto-Regular.ttf");
tv.setTypeface(tf);
tv1.setTypeface(tf);
tv.setText(artist);
tv1.setText(title);
}
1) In AsyncTask class how can i assign text to Taxt-view.
You can't because you are not running on UIThread. IMHO best practice is to return this value to the UIThread using onPostExecute(). Another choice is to use runOnUIThread() with your own Runnable class.
2) How can i call that Async-task class within a specific time, like
30sec time interval.
You can use ScheduledExecutorService.
3) There are some text which i need to fetch only once in a day. so
how can check that when last time data was fetched?
Save the last time into your DB.
the best method to store that fetch data and use it to Listview?(i
mean i need to store that that into database or any hashmap or array
list?)
Subclass (extend) ArrayAdapter for your own needs, then you can directly use it to feed the ListView. Here's an example.
Using a handler, you could create a runnable to run every 30 seconds using postDelayed.
Related
I am using a singleton for fetching data from a web service and storing the resulting data object in an ArrayList. It looks like this:
public class DataHelper {
private static DataHelper instance = null;
private List<CustomClass> data = null;
protected DataHelper() {
data = new ArrayList<>();
}
public synchronized static DataHelper getInstance() {
if(instance == null) {
instance = new DataHelper();
}
return instance;
}
public void fetchData(){
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions options = new QueryOptions();
options.setSortBy(Arrays.asList("street"));
query.setQueryOptions(options);
CustomClass.findAsync(query, new AsyncCallback<BackendlessCollection<CustomClass>>() {
#Override
public void handleResponse(BackendlessCollection<CustomClass> response) {
int size = response.getCurrentPage().size();
if (size > 0) {
addData(response.getData());
response.nextPage(this);
} else {
EventBus.getDefault().post(new FetchedDataEvent(data));
}
}
#Override
public void handleFault(BackendlessFault fault) {
EventBus.getDefault().post(new BackendlessFaultEvent(fault));
}
});
}
public List<CustomClass> getData(){
return this.data;
}
public void setData(List<CustomClass> data){
this.data = data;
}
public void addData(List<Poster> data){
this.data.addAll(data);
}
public List<CustomClass> getData(FilterEnum filter){
if(filter == FilterEnum.NOFILTER){
return getData();
}else{
// Filtering and returning filtered data
}
return getData();
}
}
The data is fetched correctly and the list actually contains data after it. Also, only one instance is created, as intended. However, whenever I call getData later, the length of this.data is 0. Because of this I also tried it with a subclass of Application holding the DataHelper object, resulting in the same problem.
Is there a good way of debugging this? Is there something like global watches in Android Studio?
Is there something wrong with my approach? Is there a better approach? I am mainly an iOS developer, so Android is pretty new to me. I am showing the data from the ArrayList in different views, thus I want to have it present in an the ArrayList as long as the application runs.
Thanks!
EDIT: Example use in a list view fragment (only relevant parts):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filter = FilterEnum.NOFILTER;
data = DataHelper.getInstance().getData(filter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
customClassListAdapter = new customClassListAdapter(getActivity(), data);}
EDIT2: Added code where I fetch the data from Backendless, changed reference of DataHelper to reference of data in first EDIT
EDIT3: I usa a local EventBus for notifying the list view about the new data. This looks like this and works (initially the data gets populated, but after e.g. applying a filter, the ArrayList I get with getData is empty):
#Subscribe
public void onMessageEvent(FetchedDataEvent event) {
customClassListAdapter.notifyDataSetChanged();
}
Try instead of keeping reference to your DataHelper instance, keeping reference to your list of retrieved items. F.e. when you first fetch the list (and it's ok as you say), assign it to a class member. Or itarate through it and create your own array list of objects for future use.
Okay I finally found the problem. It was not about the object or memory management at all. Since I give the reference on getData to my ArrayAdapter, whenever I call clear (which I do when changing the filter) on the ArrayAdapter, it empties the reference. I basically had to create a copy of the result for the ArrayAdapter:
data = new ArrayList<>(DataHelper.getInstance().getData(filter));
I was not aware of the fact that this is a reference at all. So with this the data always stays in the helper entirely. I only did this because this:
customClassListAdapter.notifyDataSetChanged();
does hot help here, it does not call getData with the new filter again.
Thanks everyone for your contributions, you definitely helped me to debug this.
It is likely that getData does get called before the data is filled.
A simple way to debug this is to add (import android.util.Log) Log.i("MyApp.MyClass.MyMethod", "I am here now"); entries to strategic places in fetchData, addData and getData and then, from the logs displayed by adb logcat ensure the data is filled before getData gets called.
I have problems with populating a spinner list with multiple Async Task to retrieve data.
The first question is to put show the progress dialog and hide it ? (Problem with multiple async task)
And is it correct to create and set new adapter every time ?
Here is my code
public void getEntities(Entity account) {
final ArrayList <Entity> entities = new ArrayList <Entity>();
Log.i(TAG, String.valueOf(account.getRelationships().size()));
for(final Relationship relationship : account.getRelationships()) {
new SOAPCall() {
#Override
protected void onPostExecute(String soapResponse) {
final DataMap dm = ParseSoapResponse.parseEntity(soapResponse);
Entity ent = new Entity(dm.getDataMapArrayList("items").get(0).getString("displayName"));
ent.setLogicalName(relationship.getReferenceEntity());
entities.add(ent);
spinnerAdapterEntities = new ArrayAdapter <Entity> (getApplicationContext(), android.R.layout.simple_spinner_item, entities);
spinnerAdapterEntities.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
_spinner_entities1.setAdapter(spinnerAdapterEntities);
}
}.execute(SoapRequest.getInstance(getBaseContext()).getRetrieveEntityRequest(relationship.getReferenceEntity(), "Entity"));
}
}
AsyncTasks are quite inefficient, so I would suggest using only one AsyncTask. That would solve all your current problems. You would show progress in onPreExecute, loop trough Relationships in doInBackground, update your adapter in onProgressUpdate and hide progress in onPostExecute.
Override your AsyncTask class with onPreExecute() and onPostExecute() methods.
Example :
private class DoSomething extends AsyncTask<Void, Void, Void> {
ProgressDialog pd;
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(context, null, "Loading...");
}
#Override
protected Void doInBackground(Void... params) {
// Do something here...
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
pd.dismiss();
}
}
With a simple hack in your case, since it is a multiple AsyncTask, init your ProgressDialog in your SOAPCall constructor, and in onPostExecute() of every AsyncTask, check if ProgressDialog is null. If is not null, then call ProgressDialog.dismiss(), else, do nothing.
You do not set a new ADAPTER every time, it is hard to control the data source (ArrayList). Make a custom Spinner adapter, implement your own ArrayList update method, then call SpinnerAdapter.notifyDataSetChanged() when you update your array list.
P.S : It is a very bad practice, hence, do stick to ONE AsyncTask only.
I don't understand how is the Parse working?
I download data in parse to my arraylist , but when I show the Pets.size inside (//here) method "done" it will show 4, but when I show pets.size outside the done's method it will show 0?
public class Test extends AppCompatActivity {
ArrayList<Pet> pets;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
pets = new ArrayList<>();
ParseQuery<Pet> query = new ParseQuery<>("Pets");
query.findInBackground(new FindCallback<Pet>() {
#Override
public void done(List<Pet> list, ParseException e) {
if (e!=null){
Toast.makeText(Test.this,"Error",Toast.LENGTH_LONG).show();
}
for (Pet pet : list){
Pet newPet = new Pet();
newPet.setName(pet.getName());
newPet.setType(pet.getType());
pets.add(newPet);
}
// here
}
});
Toast.makeText(Test.this,"You have "+pets.size()+" pets",Toast.LENGTH_LONG).show();
}
Here's my Pet class:
#ParseClassName("Pets")
public class Pet extends ParseObject {
public String getName(){
return getString("name");
}
public void setName(String name) {
put("name", name);
}
public String getType(){
return getString("type");
}
public void setType(String type) {
put("type", type);
}
}
And an orther question , what should I do if I wanna save the data in local data?
Explanation:
findInbackground performs an operation to find all ParseObjects in a background thread (outside the main thread, or UI thread). So when it completes in the place where you have the comment
//here
That is when the background thread finishes it's call to find the objects. When you try to look at the size of the array outside that call where it shows size of 0, it is because it reached that point before the background thread finishes it's work (of adding to your array from objects it found).
What is happening is the operation for find() is happening in parallel with your main threads code.
And for your second question, make sure you enableLocalDatastore and then you can pin results from queries to your local cache. This data is stored on the device until the user deletes your app or clears cached data in settings.
Follow this guide to setup local cache Local Datastore with Parse
Note: A solution to your problem for when the background task of finding the pets is complete is to call a method from within the Callback for the findInBackground call that will handle the newly found Pet ParseObjects. Also remember to handle if the query fails either by finding no objects or some failure in connection / timeout.
just calling pet.pin() or pet.pinInBackground(); you can save a parseObject in local storage , to query objects in local storage you need set query.fromPin(true)
https://parse.com/docs/android/guide#objects-the-local-datastore
"done" method fires when the background task ends.
I feel like a broken record.
After many attempts, I have failed at getting a listview through Parse data to display a specific set of information.
Here is my model...this is all data from users:
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
public Midwifefirm() {
// A default constructor is required.
}
//practice name
public String getPracticeName() {
return getString("practicename");
}
public void setPracticeName(String practicename) {
put("practicename", practicename);
}
//education
public String getEducation() {
return getString("education");
}
public void setEducation(String education) {
put("education", education);
}
//years in practice
public String getYearsinPractice() {
return getString("yearsinpractice");
}
public void setYearsinPractice(String yearsinpractice) {
put("yearsinpractice", yearsinpractice);
}
//practice philosophy
public String getPracticePhilosophy() {
return getString("practicephilosophy");
}
public void setPracticePhilosophy(String practicephilosophy) {
put("practicephilosophy", practicephilosophy);
}
I have this adapter; I am wondering what to place in the query section, as I just want to pull the data into the ListView that is defined in the data model:
public class CustomMidwifeAdapter extends ParseQueryAdapter<Midwifefirm> {
public CustomMidwifeAdapter(Context context) {
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
}
#Override
public View getItemView(Midwifefirm midwifefirm, View view, ViewGroup parent) {
if (view == null) {
view = View.inflate(getContext(), R.layout.activity_midwife_result_list, null);
}
//use midwifefirm as item view/list
super.getItemView(midwifefirm, view, parent);
// find in layout the practice name
TextView titleTextView = (TextView) view.findViewById(R.id.practicename);
//in the midwifefirm data model, call getPracticename
titleTextView.setText(midwifefirm.getString("practicename"));
// Add education view
TextView EducationView = (TextView) view.findViewById(R.id.education);
EducationView.setText(midwifefirm.getString("education"));
// Add yearsexperience view
TextView ExperienceView = (TextView) view.findViewById(R.id.yearsinpractice);
ExperienceView.setText(midwifefirm.getString("yearsinpractice"));
//Add practice philosophy view
TextView PracticePhilosophyView = (TextView) view.findViewById(R.id.practicephilosophy);
PracticePhilosophyView.setText(midwifefirm.getString("practicephilosophy"));
return view;
}
}
And here is the Main Activity:
public class MidwifeResultList extends ListActivity {
private ParseQueryAdapter<ParseObject> mainAdapter;
private CustomMidwifeAdapter midwifeListAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Initialize the subclass of ParseQueryAdapter
midwifeListAdapter = new CustomMidwifeAdapter(this);
// Default view is all meals
setListAdapter(mainAdapter);
}
Every time I run this, I get no results.
Thanks in advance for any help
Michael
I can tell you why I think it fails now and I can tell you why I'm very sure it will fail after you sort out the current issue.
It seems that you're trying to use different classes
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
and
ParseQuery query = new ParseQuery("Midwives");
You need to be consistent and use the same name. Either use Midwives or Midwifefirm for both. Let's assume you picked the latter. You're also saying
all that is stored in the user table...wasn't sure if I needed to create new tables.
The query above wants to get all entries of type Midwives. If there's no such type, it'll return nothing. So you have two options:
In you Parse dashboard, reate a class Midwifefirm (don't forget to update the String inside #ParseClassName above) and store your Midwifefirm data in there. You don't need to change your query for this.
Add a column to your ParseUser class, such as type, that you can set to Midwifefirm or whatever if that user is a Midwifefirm or whatever. Then in your query you need to add:
ParseQuery query = new ParseQuery("Midwives");
query.whereEquals("type", "Midwifefirm");
I greatly prefer the former.
Anyway, once your done that, the issue is that you're not using a custom view for this. You're relying on the one provided by Android by default for ListActivity. I am fairly sure it doesn't have any of the fields you're after, so you should create a custom view for this, then at the top of onCreate in your Activity make sure you use it
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_custom_view);
By the way, the following are redundant if you populate then in getItemView anyway:
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
One final advice: if you're still having issues, set breakpoints and do some investigations first. What you need to check is:
Whether you get anything at all from Parse when you do your query. Your adapter has an useful addOnQueryLoadListener that you may use to check whether anything's been retrieved at all.
If stuff is retrieved successfully, you need to check whether the list view is populated correctly. Again, use breakpoints, this time in getItemView maybe.
I'm going to do a wild guess here using the lovely brainwrecking API help of Parse.com about ParseQueryAdapters
Before continuing, may I mind you that my experience with ParseQueryAdapters is a minimum but I think I have a basic knowledge about them + I have some experience with Parse on its own. ANYHOW,
As an example they use both these
final ParseQueryAdapter adapter = new ParseQueryAdapter(this, "Midwives");
adapter.setTextKey("name");
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
and
// Instantiate a QueryFactory to define the ParseQuery to be used for fetching items in this
// Adapter.
ParseQueryAdapter.QueryFactory<ParseObject> factory =
new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery create() {
ParseQuery query = new ParseQuery("Midwives");
return query;
}
};
// Pass the factory into the ParseQueryAdapter's constructor.
ParseQueryAdapter<ParseObject> adapter = new ParseQueryAdapter<ParseObject>(this, factory);
adapter.setTextKey("name");
// Perhaps set a callback to be fired upon successful loading of a new set of ParseObjects.
adapter.addOnQueryLoadListener(new OnQueryLoadListener<ParseObject>() {
public void onLoading() {
// Trigger any "loading" UI
}
public void onLoaded(List<ParseObject> objects, ParseException e) {
// Execute any post-loading logic, hide "loading" UI
}
});
// Attach it to your ListView, as in the example above
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
To start of, the reason why I think nothing is loading inside your list has to do with a little mixup between the initilization of your ParseQueryAdapter and your custom adapter.
You configure the basic adapter, and also initialize a custom adapter but you don't do anything with the custom adapter, tho the custom adapter seems to contain the logics to load your data model.
I think what you're looking for is something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new CustomMidwifeAdapter<ParseObject>(this);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Default view is all meals
setListAdapter(mainAdapter);
}
All you need to pass is the context (aka "this"), and the constructor of your custom class will handle the factory internal
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
Tho to be honest since you do:
new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
I wonder if you'd need to change your "QueryFactory" to
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery(MidWifefirm.class);
return query;
}
});
Where you pass a class to the the query rather than the tableName, but I could be wrong on that one.
Either way I hope this has helped in some way!
I am currently having trouble getting a value from an AsyncTask that gets data from a JSON connection. I have looked at a few examples, but mostly I have only seen posting results from AsyncTask.
First I have an object called Dog that only has a String, Name. I am trying to get the Name of the dog from the server.
Using the following code in my oncreate, I start the DogAsyncTask while assing in an URL called n and a Dog d_in.
Dog d_in = new Dog("DogName");
DogAsyncTask task = new DogAsyncTask(d_in);
String n = "www.dog.com";
task.execute(n);
Log.e("Out", d_in.getName());
My AsyncTask is as follows:
private class DogAsyncTask extends AsyncTask<String, Void, String> {
Dog d = null;
DogAsyncTask(Dog d){
this.d = d;
}
ProgressDialog mProgressDialog;
#Override
protected void onPostExecute(String result) {
d.setName(result);
Log.e("Dog", d.getName());
mProgressDialog.dismiss();
}
#Override
protected void onPreExecute() {
mProgressDialog = ProgressDialog.show(AsyncTestActivity.this, "Loading...", "Data is Loading...");
}
#Override
protected String doInBackground(String... name) {
//Go to url = name and then gets String below.
String outfromjson = "new dog name"; //This will be a function that gets a name from JSON
return outfromjson;
}
}
I tried using something like
Log.e("Out", task.d.getName());
but I keep getting the default name of the dog which is "DogName". How to I carry values out of AsyncTask?
OK, The thing what is happening here is:
You want to pass Dog as reference (pointers as in C / C++ / Objective-C),
and you want to reflect change in original variable using other variable.
but keep in mind that Java doesn't pass object by reference, Java passes objects by value (only value is transferred, another object is created and assigned value).
So what you can do is you can assign that result-holder object again as a value to the original object.
You can do the following,
Declare and implement an interface in your activity and then use it's methods as a callback from the onPostExecute method.