I want to make an HTTP Request inside the app widget I've built.
The idea is that every couple of hours the widget will update itself by making an HTTP Request and use the information of it to update its views.
I've already tried to use a Thread in onUpdate function, the HTTP Request is working just fine, the problem is that I can't use runOnUiThread() to update the views, because I have no parent activity to run this function on.
Is there another way to update the widget's views inside the Thread?
Sample code:
public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
final int appWidgetId = appWidgetIds[i];
final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_card);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
Http.Response resp = Http.postData("http://www.example.com", null, null, null);
views.setTextViewText(R.id.textview, resp.body);
}
catch (Exception ex) {
Log.e(TAG, Log.getStackTraceString(ex));
}
}
}
}
Thank you upfront.
I've already tried to use a Thread in onUpdate function
That is not a very good idea. Your process can be terminated before your work completes. Instead, create an IntentService that does the network I/O and updates the app widget via an AppWidgetManager.
the problem is that I can't use runOnUiThread() to update the views, because I have no parent activity to run this function on
Since you do not have any views to update, you do not need to worry about this. You can set up the RemoteViews and hand them to the AppWidgetManager on a background thread.
Related
I am trying to make one of the views in android widget visible for a second, but so far no success. Can someone point me in the right direction, please?
I've tried to do it with animation, timer but so far I got the nearest with runnable. Well, kind of as I get System.out.println("run test") executed but views are not updated.
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (MinutesClicked.equals(intent.getAction())){
//your onClick action is here
///////////////////////////////////////////////////////////
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
final RemoteViews views;
ComponentName appWidget;
views = new RemoteViews(context.getPackageName(), R.layout.nixie_clock);
appWidget = new ComponentName(context, NixieClock.class);
final Runnable r = new Runnable() {
public void run() {
views.setViewVisibility(R.id.textDateDay, VISIBLE);
System.out.println("run test");
}
};
handler.postDelayed(r, 1000);
/////////////////////////////////////////////////////////
appWidgetManager.updateAppWidget(appWidget, views);
}
}
So far I am getting System.out.println("run test") printed but expected views are not made VISIBLE. I can make the view visible after a click but I cannot make it appear only for a second and disappear. Thank you in advance for any help.
You are updating the RemoteViews, but you are not sending the updated RemoteViews anywhere. You need to call updateAppWidget() again after updating the RemoteViews.
Note that your process might be terminated before the one-second delay elapses, in which case your view will not become visible.
So I am creating an app where I get the string from my AsyncTask which I have created as a subclass in my MainActivity class in order to get the variables I receive from the internet. And according to variables I get I change the images accordingly after every 5 seconds. Now the task is successful but on activity refresh I keep getting the default layout I created in activity_main.xml and again it changes to the one I want.
Posting my code below.
Thread thread = new Thread() { //thread I am running every 5 secs
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
syncapp sa = new syncapp(); //AsyncTask to get String from Internet
sa.execute();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(), MainActivity.class); //Creating intent to restart the activity (Need a workaround if possible)
startActivity(mainActivity);
}
;
};
thread.start();
}
public void setImage(String str) //Function I will call to change Image
{
vacancy =0;
b = a.toCharArray();
for (int i = 0; i < 6; i++)
if (b[i] == '0'){
iv[i].setImageResource(R.drawable.img2);
vacancy++;}
else if (b[i] == '1') {
iv[i].setImageResource(R.drawable.img1);
}
Log.i("abc ", a);
tv.setText("InBackGround" + str);
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* for(int i =0;i<6;i++) {
outState.putChar("imv"+(i+1), b[i]);
}*/
outState.putString("a",a); //Getting a from the internet (php file)
Log.i("Saved State", "Activity A - Saving instance state");
}
Now what I want is if you have a better method to do this thing. Eg. In a Stock market application the prices keep on changing. The same way I want my image to change according to the data I get.
If this is the only method then how do I save changes I make
(eg. like in the code above setImageResource to img2) permanently.
If I can use something else ImageView.
I have already used onSaveInstanceState But as I am taking values from the internet I don't know I am not able to use them.
So first of all.. when working with UI elements such as Views, Widgets, you would want to avoid spawning your own threads, as View can be touched only from the thread it was created from.
Second.. you would also want to avoid sleeping inside your worker thread - basically just use the Handler class (from android.os) and post a delayed Runnable, like so: https://stackoverflow.com/a/20784353/2102748. Just be sure to stop all work on that particular handler when your Activity stops, like so: https://stackoverflow.com/a/3627399/2102748
Third - you should perhaps load the photo immediately (on the same AsyncTask or thread) if that is the only thing you need, but be sure to post the "set bitmap to view" work to the handler you created.
Hope this helps.
I wrote a collection widget to show calllogs in home screen.
So there may have 2 receivers exist: one is widget provider the other is call log content observer. Im not sure whether content observer is a receiver or not.
CallLogAppWidgetProvider is declared in manifest.xml and I need to store some data in this receiver object (for using later in observer).
CallLogContentObserver is constructed and registered when WidgetProvider triggered. Observer is triggered when calls are made.
The problem is, when I first place my widget, widgetprovide is constructed, triggered and onUpdate is called, I can see CallLogAppWidgetProvider.mContext and CallLogAppWidgetProvider.mWidgetIds is set in app's main thread(tid=8148,pid=8148).
but when calls are made, CallLogContentObserver.onChange is called in the same thread(tid=8148,pid=8148). Inside onChange, mContext has the value that set previously but mWidgetIds is null, so mywidget could not reflect calllog's changes.
According to this post: Save data in Broadcast receiver , my widgetprovider may be destroyed when it returned, so it could explain why mWidgetIds is null, but conflict with mContext stored old value. And that post did not explain why we need persist our data in receivers.
If widgetprovider is destroyed, then when observer triggered how did android start to run observer's onChange? Is it the same mechanism like closures in lua? Why mContext can store a value?
If widgetprovider is not destroyed, then why mWidgetIds is null when observer runs? I dont know exactly how java passes primitive array, according to this post: Is an array a primitive type or an object (or something else entirely)? , arrays are runtime classes created by jvm, so mWidgetIds is a strong reference, the integer array mWidgetIds points to can not be gc after provider returned, because at least mWidgetIds points to it. What happend when provider returned?
Could you please tell me what on earth happend behind thses scenes? Any information will be precitated, thank you.
code is below:
public class CallLogAppWidgetProvider extends AppWidgetProvider {
private static final String TAG = "CallLogAppWidgetProvider";
private ContentObserver mContentObserver = null;
private Handler mHandler = new Handler();
private Context mContext = null; //data to store
private int[] mWidgetIds; //data to store
protected class CallLogContentObserver extends ContentObserver {
public CallLogContentObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
#Override
public void onChange(boolean selfChange) {
AppWidgetManager.getInstance(mContext).notifyAppWidgetViewDataChanged(mWidgetIds, R.id.calllog_list);
}
}
#Override
public void onEnabled(Context context) {
mContext = context; //store data when first place in home screen
mContentObserver = new CallLogContentObserver(mHandler);
context.getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, mContentObserver);
}
#Override
public void onDisabled(Context context) {
context.getContentResolver().unregisterContentObserver(mContentObserver);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
mWidgetIds = appWidgetIds; //store data when first place in home screen
for (int i = 0; i < appWidgetIds.length; i++) {
Intent intent = new Intent(context, CallLogRemoteViewsService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calllog_appwidget);
rv.setRemoteAdapter(R.id.calllog_list, intent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
i am writing here to ask you a litlle question about the progress dialog.
In fact, i call web service in my application, the progress dialog works very well.
But when i change the date of my object to do the call of my web service,
the progress dialog appear, the results are receiveid. AND automatically, there is a new call of the web service and the progress dialog didn't dissmiss :-/
On my server JBoss, i can see the calls from my application.
At the first create of my activity i have one call.
But at the second call, when i changed the date, there are two call. i don't know why because when i change one of my spinner its works fine, the web service is call and returns the results.
I call the web service in a asynctask. I do nothing with the progress dialog in the asynctask.
Here is the method where i call the progress dialog
public void callWebService()
{
myProgressDialog = ProgressDialog.show(OverviewMoney.this,"", "Récupération liste des positions cash",true);
final Runnable runInUIThread = new Runnable() {
public void run() {
setListView();
}
};
new Thread() {
#Override public void run()
{
loadListMoney();
myProgressDialog.dismiss();
uiThreadCallback.post(runInUIThread);
myProgressDialog= null;
}
}.start();
}
The method loadListMoney call my web service in a asynctask and the method setListView put my resultList in an adapter to print the list on a list view.
Thanks in advance :-) (And sorry for my english)
Try this
public void callWebService()
{
if (myProgressDialog != null) myProgressDialog.dismiss();
myProgressDialog = ProgressDialog.show(OverviewMoney.this,"", "Récupération liste des positions cash",true);
final Runnable runInUIThread = new Runnable() {
public void run() {
setListView();
}
};
new Thread() {
#Override public void run()
{
loadListMoney();
myProgressDialog.dismiss();
uiThreadCallback.post(runInUIThread);
myProgressDialog= null;
}
}.start();
}
According to my understanding, when this function is called twice 1st time it will start progress dialog and quickly another function call is placed so without dismissing previous progress dialog you were creating one more progress dialog. So that was the main cause of it.
I have a widget which needs to do some operations in another thread. It has a method which processes some data and shows the result in a TextView on the widget.
To implement this, I need to call my method in a separate thread. Then I need to include some code to the end of that method to show the results on the widget (something like textView1.setText("my results");. This has to be done in a UI thread. I want to use the method runOnUiThread. But it exists only for an activity. Is there an analog of this method for widget class? What can I do to use that method in my widget?
Here is my code:
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
//...
//here I need to launch a method toBeLaunchedInASeparateThread in a separate thread
}
public void toBeLaunchedInASeparateThread()
{
// ... many operations
int result = 0;
//here I need to launch the following line in the UI thread to follow Android guidelines
setTextToTextView(context, "My result is " + result);
}
public static void setTextToTextView(Context context, String text)
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_sample);
views.setTextViewText(R.id.widgetTextViewCard, text);
ComponentName myWidget = new ComponentName(context, MyWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(manager.getAppWidgetIds(myWidget), views);
}
}
you can use an Handler, to connect with the UI Thread and post on its queue. Handler.post(Runnable). Here the documentation
blackbelt's answer is great, however you can also use textView1.post(runnable) and methods like that. This way you don't have to create a Handler instance, and the code will run on UI thread. AFAIK both solution will do the exactly same things.
For RemoteViews:
Handler mHandler = new Handler();//this must be ran on the UI thread.
....
mHandler.post(new Runnable(){
public void run() {
toBeLaunchedInASeparateThread();
}
});