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();
}
});
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.
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 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.
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.
Lets say I have multiple running Activities; A, B, and C. Each share a similar optionsmenu with some differences in their execution ("start" option might be slightly different in Activity A than B). I also have a static class called "values" that is tied to activity A. It also has the context of the current running activity.
At times values may call an item in the optionsmenu of the current running activity. My code is messy(see below) so I would like to organize it to a more readable form.
My goal is to set up values so that it can call just a function of the current running activity instead of an optionmenu item of that activity. Inside the activity an item of the optionsmenu would just call a function instead of code inside of it (for organization reasons).
Here a sample of values.class calling the item of an optionsmenu of the current running activity.
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
((Activity) getCurrentContext()).openOptionsMenu();
((Activity) getCurrentContext()).closeOptionsMenu();
((Activity) getCurrentContext()).onOptionsItemSelected(theMenu.findItem(R.id.start));
}
});
}
};
new Thread(startRun).start();
}
As you can see values.startExample() calles the start item of the options menu of the current running activity. I would like to change this so that it calls a function based off of the current running activity instead. So I was thinking that I could do something like this instead.
In values.class
ActivityB b = new ActivityB
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
if( ((Activity) getCurrentContext()).getClass().getName().equals("package.ActivityB") ) {
b.start();
}
}
});
}
};
new Thread(startRun).start();
}
And Activity B might look like.
public class ActivityB extends Activity {
//class code here
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case start:
this.start();
break;
}
}
public void start() {
//code here
}
}
Is this possible? I know that this question might sound confusing so please ask questions and I might be able to simplify it again.
No no no no no. This is like pulling a crowbar to Android and just smashing it to pieces. Activities are not ever meant to be instantiated via new.
If all you want is for there to be some shared behavior between the methods that the different Activities call in their onOptionsItemSelected(), then either create a super activity and call the super before you add whatever behavior you want in the particular subclasses or else create a function accessible to all activities that contains what doesn't change and then add what does change inside of the subclass. Either way, Activities are meant to be ran via providing a reference to the class in startActivity, etc. You can't treat an Activity like an ordinary java object (even though it ultimately is) because it has a lifecycle that is carefully managed by the system.