So I'm trying to make a youtube application by retrieving the json data from a youtube channel. Problem is I'm great at writing single-threaded applications. But when it comes to multithreading I always lose my insight.
I haven't learned it in school yet and most tutorials are rubbish, or atleast I find that.
So what I want to do is introduce a thread to my "GetVideos" class so it doesn't slow down my application while retrieving the videos. I know I'll have to use a handler and thread but everytime I try to use them my application crashes. Can you guys help?
public class GetVideos { //class that retrieves JSON data based on youtube username
private String channelName;
private HttpClient client; //client that gets info
private VideoLibrary lib;
public GetVideos(String channelName) {
this.channelName = channelName;
client = new DefaultHttpClient();
lib = new VideoLibrary();
fillData();
}
private void fillData() {
try {
final String URL = "http://gdata.youtube.com/feeds/api/users/" + channelName + "/uploads?v=2&alt=jsonc";
HttpGet get = new HttpGet(URL);
HttpResponse response = client.execute(get);
String jsonString = EntityUtils.toString(response.getEntity());
JSONObject json = new JSONObject(jsonString);
JSONArray jsonArray = json.getJSONObject("data").getJSONArray("items");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject items = jsonArray.getJSONObject(i);
String title = items.getString("title");
String thumbUrl = items.getJSONObject("thumbnail").getString("sqDefault");
String url = items.getJSONObject("player").getString("default");
lib.addVideo(new Video(title, url, thumbUrl));
}
} catch (JSONException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (ClientProtocolException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (IOException e) {
e.printStackTrace();
}
}
public VideoLibrary getLib() {
return lib;
}
}
public class SxePhil extends Activity { //class that makes up the interactive end of
//the app, here the JSON data is put in a list
private ListView list;
private GetVideos g;
private VideoLibrary videoLibrary;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sxephil);
list = (ListView) findViewById(R.id.sxephilList);
g = new GetVideos("sxephil");
videoLibrary = g.getLib();
ArrayList<String> titles = new ArrayList<String>();
for (int i = 0; i < 25; i++) {
titles.add(videoLibrary.getVideos().get(i).getTitle());
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.simplerow, titles);
list.setAdapter(adapter);
}
}
Basically what I tried was implementing the threading that is used here: http://blog.blundell-apps.com/show-youtube-user-videos-in-a-listview/
in my code, because this project was built on an older sdk, so I wanted to modernize it so to speak. Look at the GetYoutube*** class and MainActivity class that's where the money is
You should either use an AsyncTask or even better, a Loader, they automatically handle the treads for you
First : basic advices (that you MUST remeber and follow)
Never do processing in a class constructor (that's bad OO design, imho)
Never, ever leave time-consuming process in Activity.onCreate method (and in general, in any method that is runned by the UI Thread, ie. onResume(), onClick(), ...)
mario and runor49 were right : you should use an AsyncTask here (and you must read and understand http://developer.android.com/guide/components/processes-and-threads.html#WorkerThreads. If you don't, read it again...)
Basically, you should do the following :
onCreate must launch a separate thread, which will contain the "long" processing (load the data from network)
When done, this thread must call the Activity.runOnUiThread(Runnable), where the runnable will create the Adapter and assign it to the ListView.
Here is a code sample :
#Override
public void onCreate(Bundle savedInstanceState) {
...
loadDataInThread();
}
private void loadDataInThread() {
new Runnable() {
#Override
public void run() {
// LOAD DATA FROM NETWORK
displayDataInUIThread(datas);
}
}.start();
}
private void displayDataInUIThread(final VideoLibrary data) {
runOnUiThread(new Runnable() {
#Override
public void run() {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.simplerow, data);
list.setAdapter(adapter);
}
});
}
This is a very basic (and rude) way to do it, and the AsyncTask way () way will be far better, but it should be working and fill the contract...
You can accomplish this most easily using an AsyncTask. Check out this tutorial.
http://android-developers.blogspot.com/2009/05/painless-threading.html
Related
I am using the Facebook graph api to find out what pages a user is apart of. When the query comes back with a json object it has what I need but, for some reason it doesn't want to add to my array list. The correct value is printed in log.d it seems to skip my arraylist for some reason. Any ideas?
Find page function
private ArrayList<String> foundPages;
private JSONObject jsonObject;
public ArrayList<String> findPages()
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
return foundPages;
}
There is a common way to solve this problem, which is to define a callback method which will return these values to you, AFTER they have been populated by the call, which goes something like this (my java is rusty, bear with me...)
define an interface :
interface Callback{
void apiResponseCallback(ArrayList<Page> result);//whatever your model is, make the array of that type
}
then, in your normal findPages method, change it to this:
public void findPages(Callback callback) {
//
//
........
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);//here we are returning the data when it is done
}
then, when you call findPages
findPages(new Callback() {
#Override
public void apiResponseCallback(ArrayList<Page> result) {
here, this result parameter that comes through is your api call result to use, so result will be your populated pages to use.
}
});
}
sake of completeness:
public void findPages(Callback callback)
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
}
Yep. This here:
request.executeAsync();
triggers an asynchronous request. But your "current" thread simply continues to do:
return foundPages;
and it returns an empty list.
That list gets later filled, but at the moment in time when that method returns, that list is still empty. Or just gets filled. Who knows, as it gets filled asynchronously, at some unknown point in the future.
A solution could be to have some other variable/field that tells you the data has arrived and pushed into the list.
Alternatively, that method could just make a synchronous request, simply block the caller from progressing until the data has arrived.
You see, you can't have it both ways: when you don't wait for your results to arrive, you shouldn't expect them to be available immediately.
I have a custom adapter that buttons and TextView, the TextView is changed on a button click within listview and it after sending and receiving feedback from http Post via Json response, I tried using runnable and assynctask but no success. In the runnable, I can not return a value from the method.
What I wanted is to send http request to the server and return json results, based on the returned results, the TextView will change.
What is the best approach to use to achieve this.
If any one can help point me to a resource that will help me to achieve this will be highly welcome.
Thanks in advance!
Here is my code..
public String getStatus(final String id, final String user){
final String[] resp = {"0/0/0"};
new Thread(new Runnable() {
#Override
public void run() {
// Building Parameters
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", user));
params.add(new BasicNameValuePair("id", id));
Log.d("Nay Params", "" + params);
// getting product details by making HTTP request
JSONObject json = jsonParser.makeHttpRequest(NAY_URL, "POST",
params);
// check your log for json response
// json success tag
try {
Log.d("Nay Response", ""+ json);
success = json.getBoolean(TAG_SUCCESS);
yeaSt = json.getBoolean(TAG_E_STATUS);
naySt = json.getBoolean(TAG_A_STATUS);
yeaC = json.getInt(TAG_YEA);
nayC = json.getInt(TAG_NAY);
if (success){
resp[0] = yeaS + "/" + naySt + "/" + yeaC + "/" + nayC;
return resp[0];
//Can not return return value within void method that is; Class void run()
}
} catch (JSONException e) {
e.printStackTrace();
//return resp[0];
}
}
}).start();
//Can not acces json result outside the try catch block
Log.d("sux", ""+success);
// Log.d("Rest1", resp[0]);
return resp[0];
}
You could use Callable interface in order to run a thread and get the thread's result.
Here's the Callable documentation.
And Jakob explains how to use it here.
Hope that helped, if you need a concrete example I recently used this approach in my project, and I'll be happy to provide you with a sample.
Edit:
Here is a simple example of using callable, I changed a little bit my code so it is more clear:
public class Test {
ExecutorService executor;
public Test() {
/* I used an executor that creates a new thread every time creating a server requests object
* you might need to create a thread pool instead, depending on your application
* */
executor = Executors.newSingleThreadExecutor();
}
private JSONObject doTheWork() {
// init
Callable<JSONObject> callable;
Future<JSONObject> future;
JSONObject jsonResult = null;
try {
// create callable object with desired job
callable = new Callable<JSONObject>() {
#Override
public JSONObject call() throws Exception {
JSONObject jsonResult = new JSONObject();
// connect to the server
// insert desired data into json object
// and return the json object
return jsonResult;
}
};
future = executor.submit(callable);
jsonResult = future.get();
} catch(InterruptedException ex) {
// Log exception at first so you could know if something went wrong and needs to be fixed
} catch(ExecutionException ex) {
// Log exception at first so you could know if something went wrong and needs to be fixed
}
return jsonResult;
}
}
Another approach would be creating a class that implements Callable. Whatever of these approaches you choose depends on your application, and maybe person's taste :).
Happy that I helped.
I'm a Newbie, so, there are a lot of things that I don't know.
I'm building my first application, using PHP and Java, and my
Database is allocated on Phpmyadmin.
To get the data that I want, I have to create a PHP Query, and output
everything using Json. To get the Json Data I use Java.
I have been searching about my question for hours, and unfortunately,
I couldn't find a specific way to what I want, in my situation. I
would like to update my ListView automatically when a new record is
inserted into my Database.
Lets imagine that you are watching a Listview, and when the owner
from the application inserts a new record on the table that shows the
Listview data, automatically the Listview shows a new record.
My problem is that I am using Json to get the Data, and I can't
change that. And I would like to find a solution for that.
So, I would like to know if it is possible or not to do that. I will
post my full code here:
Json Tags - Calling the URL and the Json Tags here:
public class ListUsersActivity extends ListActivity {
private ProgressDialog pDialog;
private ImageView img;
// URL to get contacts JSON
private static String url = "http://192.168.1.67/example/db_config.php";
// JSON Node names
private static final String TAG_CONTACTS = "contacts";
private static final String TAG_ID = "id";
private static final String TAG_NAME = "name";
private static final String TAG_IMAGE = "pic_url";
private static final String TAG_USERID = "user_id";
private static final String TAG_BIRTH_DATE = "birth_date";
// contacts JSONArray
JSONArray contacts = null;
// Hashmap for ListView
ArrayList<HashMap<String, String>> contactList;
OnCreate - Calling the Asynctask function, and creating a new arraylist for my Contact list
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lobby);
contactList = new ArrayList<HashMap<String, String>>();
ListView lv = getListView();
// Calling async task to get json
new GetContacts().execute();
}
Asynctask - Getting all the Data json
private class GetContacts extends AsyncTask {
#Override
protected void onPreExecute() {
super.onPreExecute();
// Showing progress dialog
pDialog = new ProgressDialog(ListUsersActivity.this);
pDialog.setMessage("Please wait...");
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
// Creating service handler class instance
NewSession app = (NewSession) getApplication();
String username = app.getUsername();
ServiceHandler sh = new ServiceHandler();
// Making a request to url and getting response
String jsonStr = sh.makeServiceCall(url + "?id=" + username, ServiceHandler.GET);
Log.d("Response: ", "> " + jsonStr);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
contacts = jsonObj.getJSONArray(TAG_CONTACTS);
// looping through All Contacts
for (int i = 0; i < contacts.length(); i++) {
JSONObject c = contacts.getJSONObject(i);
String id = c.getString(TAG_ID);
String name = c.getString(TAG_NAME);
String user_id = c.getString(TAG_USERID);
String birth_date = c.getString(TAG_BIRTH_DATE);
String pic_url_get = c.getString(TAG_IMAGE);
HashMap<String, String> contact = new HashMap<String, String>();
// adding each child node to HashMap key => value
contact.put(TAG_ID, id);
contact.put(TAG_USERID, user_id);
contact.put(TAG_BIRTH_DATE, getAge(birth_date) + " anos");
contact.put(TAG_IMAGE, "http://*****/images/" + user_id + "/" + pic_url_get);
// adding contact to contact list
contactList.add(contact);
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("ServiceHandler", "Couldn't get any data from the url");
}
return null;
}
Calling the adapter
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Dismiss the progress dialog
if (pDialog.isShowing())
pDialog.dismiss();
/**
* Updating parsed JSON data into ListView
* */
costumeadapter adapter = new costumeadapter(
ListUsersActivity.this,
contactList,
R.layout.list_row,
new String[]{ TAG_NAME, TAG_BIRTH_DATE, TAG_USERID},
new int[]{ R.id.name,R.id.email, R.id.id });
setListAdapter(adapter);
}
}
I tried so far
To build a Timer and refresh my GetContacts calling class every
second, but it doesn't work because the user is not able to scroll, and the application crashes
I tried to use notifyDataSetChanged but I didn't work because I dont know exactly how can i implement that.
I hope you guys can help me.
Thanks.
Of course it is possible.
To build a Timer and refresh my GetContacts calling class every
second, but it doesn't work because the user is not able to scroll,
and the application crashes
Because you're doing it on the main thread. Do it in an AsyncTask or a separate thread, that should work.
I tried to use notifyDataSetChanged but I didn't work because I dont
know exactly how can i implement that.
It's simple, you call it in the adapter: adapter.notifydatasetchanged() after you update it (more detail here).
Just use
adapter.notifydatasetchanged()
You have to add it after setListAdapter() like:
setListAdapter(adapter);
adapter.notifydatasetchanged();
I have a list that is populated by the package manager and it dieplays a list of all system apps. I need to find a way to "filter" that list so only specific apps show. I have a list of apps that I will "allow" the user to manimpulate.
Is that possible to show ONLY those apps out of all system apps? I mean filter for their name and if found on device' then show on the list?
Thank you so much for any advice! All is appreciated!
This is the relevant code that populates the list (the parent is fragment and the list is being created in async task).
private class GearAppsToDebloat extends AsyncTask<Void, Void, Void> {
private ProgressDialog progress = null;
//Using package manager to get all installed apps on the device
final List<PackageInfo> packageList = packageManager
.getInstalledPackages(PackageManager.GET_META_DATA);
final List<PackageInfo> packageList1 = packageManager
.getInstalledPackages(0);
#Override
protected Void doInBackground(Void... params) {
try {
packageList1.clear();
for (int n = 0; n < packageList.size(); n++) {
PackageInfo PackInfo = packageList.get(n);
// List only system apps
if (((PackInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM
) != 0) == true)
{
try {
packageList1.add(packageList.get(n));
// Sort App list on name basis
Collections.sort(packageList1, new Comparator<PackageInfo>()
{
public int compare(PackageInfo o1, PackageInfo o2) {
// Return sorted list of packages
return o1.applicationInfo.loadLabel(getActivity().getPackageManager()).toString()
.compareToIgnoreCase(o2.applicationInfo.loadLabel(getActivity().getPackageManager())
.toString());
}
});
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Sorry, I just did this in C# in Xamarin but I'm sure you can write the equivalent.
I got the needed applications from using a lambda expression on the GetInstalledApplications method. It selects the application if the ClassName is present in the list of apps I am looking for.
This also provided me with a very quick return, using other methods I've come across took about 8 seconds to do the same task.
const string Facebook = "com.facebook.katana.app.FacebookApplication";
const string Email = "com.android.email.Email";
const string Whatsapp = "com.whatsapp.App";
const string Twitter = "com.twitter.android.TwitterApplication";
private IEnumerable<string> AvailableApps;
private void SearchAvailableApps()
{
List<string> apps = new List<string>() { Facebook, Email, Whatsapp, Twitter };
PackageManager pm = this.Activity.PackageManager;
AvailableApps = pm.GetInstalledApplications(0).Where(x => apps.Contains(x.ClassName)).Select(x => x.ClassName);
}
You should now have the list of apps that you know the user has installed on his/her device.
I am creating an android application which has to execute web requests in the background and then handle the received data and modify the user interface according to the server response.
The goal of posting requests and handling data in the background is to avoid the freezing of user interface. Currently however I notice that the user interface is freezing so I am not sure the logic is working as it is supposed to.
Here is the part of code which is supposed to post requests and handle responses in its own thread and then pass the data to GUI:
public class ServerConnection {
Queue<String> requests;
...
DefaultHttpClient httpClient;
HttpHost targetHost;
Handler handler;
ServerResponseHandler responseHandler;
Activity activity;
public ServerConnection(Activity activity){
this.activity = activity;
this.responseHandler = (ServerResponseHandler) activity;
httpClient = new DefaultHttpClient();
targetHost = new HttpHost(TARGET_DOMAIN, 80, "http");
requests = new LinkedList<String>();
}
private Runnable requestSender = new Runnable(){
#Override
public void run() {
if(!requests.isEmpty()){
String requestString = requests.remove();
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
responseHandler.onProductsResponse(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
}
};
public void sendRequest(String requestString){
requests.add(requestString);
handler = new Handler();
handler.post(requestSender);
}
The method sendRequest() is called from the main activity which implements ServerResponseHandler. I guess the request is executed in its own thread and by calling
responseHandler.onProductsResponse(products);
the list of products (data from the web) is passed to main activity. Anyway due to poor performance I would appreciate if anyone could correct any possible issue in the logic above or suggest any other (better) option.
I'd suggest you take a look at ASyncTask class (available since Android 1.5).
It simplifies the process of creating a background Thread that synchronizes with the GUI thread once it's complete.
You should be able to achieve what you're trying using code something list this
private class DownloadFilesTask extends AsyncTask<String, List<Product>, Integer> {
protected List<Products> doInBackground(String... requestStrings) {
int count = requestStrings.length;
int results = 0;
for (int i = 0; i < count; i++) {
String requestString = requestStrings[i];
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
results++;
publishProgress(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
return results;
}
protected void onProgressUpdate(Integer... progress) {
// TODO You are on the GUI thread, and the first element in
// the progress parameter contains the last progress
// published from doInBackground, so update your GUI
}
protected void onPostExecute(int result) {
// Processing is complete, result contains the number of
// results you processed
}
}
And execute by calling
new DownloadFilesTask().execute(url1, url2, url3);
According to the handler javadoc, I don't think the post() method create any threads. If I'm right it execute the Runnable on the thread to which the handler is attached. So in this case this is the activity thread so the UI thread ! That's why you have poor performance.
You have to implement a Thread which execute your Runnable. But by doing that, you won't be able to update your activity like you currently do by calling :
responseHandler.onProductsResponse(products);
This is because you are not any more in the UI thread, and only the UI thread is authorized to interact with the UI (so the activity).
So you have to replace this call by accessing your Handler.
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putSerializable( "products", products ); //not sure it will pass here
msg.setData( bundle );
handler.sendMessage( msg );
And implementing the handleMessage() method for your Handler :
#Override
public void handleMessage( Message msg )
{
List <Product> products = msg.getData().getSerializable( "products" );
responseHandler.onProductsResponse(products);
}
Last but not least : the Handler has to still be created in the activity thread.