I am writing an Android App that checks for new messages in the background using AlarmManager and WakefulBroadcastReceiver. The Receiver starts an IntentService and that uses an AsyncTask for the network request (HTTPClient).
After a reboot, the alarm is re-enabled using a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED.
The problem occures when the onCreate method of my main Activity loads the full message contents using an AsyncTask, but only under certain circumstances:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
The doInBackground is executed and returns a String with valid content, but instead of executing onPostExecute, the excaption rises.
There are several scenarios:
I start the app from the launcher -> WORKS.
The App has been started like (1), sent to background and then a message arrives. The Notification is tapped, the Activity starts -> WORKS.
The App has been started like (1), send to background and the device was rebooted. A message arrives, the notification is tapped, the Activity starts -> EXCEPTION.
Question: How can I fix that, without burning in the developer hell for doing network requests on the UI thread?
Searching for a solution, I found onPostExecute not being called in AsyncTask (Handler runtime exception), but neither the solution from "sdw" nor from "Jonathan Perlow" changes a thing.
ServerQuery:
public abstract class ServerQuery extends AsyncTask<Void, Void, String> {
protected Context context;
String user = "ERR";
String pin = "ERR";
String cmd = "ERR";
ServerQuery(Context context, String _user, String _pin, String _cmd) {
this.context = context;
user = _user;
pin = _pin;
cmd = _cmd;
}
private ServerQuery() {
}
#Override
protected String doInBackground(Void... params) {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_client), context.getString(R.string.post_client_app)));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_user), user));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_pin), pin));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_cmd), cmd));
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://[my server hostname]/index.php");
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
} catch (UnsupportedEncodingException e) {
return "ERR";
}
HttpResponse httpResponse = null;
try{
httpResponse = httpClient.execute(httpPost);
}catch (Exception e) {
return "ERR";
}
HttpEntity httpEntity = httpResponse.getEntity();
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
} catch (IOException e) {
return "ERR";
}
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String s = null;
try {
while ((s = bufferedReader.readLine()) != null) {
stringBuilder.append(s);
}
} catch (IOException e) {
return "ERR";
}
try {
inputStream.close();
} catch (IOException e) {
return "ERR";
}
return stringBuilder.toString();
}
}
lastID:
public class lastID extends ServerQuery {
Integer lastid, savedid;
lastID(Context context, String _user, String _pin, String _cmd) {
super(context, _user, _pin, _cmd);
lastid = -1;
}
#Override
protected void onPostExecute(final String success) {
if(success.equals("ERR") || success.length() < 1) {
Toast.makeText(context, context.getString(R.string.server_no_connection), Toast.LENGTH_LONG).show();
} else {
String content[] = success.split("(;)");
if(content.length == 2) {
lastid = Integer.parseInt(content[0]);
SharedPreferences sharedPreferences = context.getSharedPreferences(context.getString(R.string.settings_filename), 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
savedid = sharedPreferences.getInt(context.getString(R.string.post_lastid), -1);
if (lastid > savedid) { // NEUES ANGEBOT!
editor.putInt(context.getString(R.string.post_lastid), lastid);
editor.commit();
// Benachrichtung
NotificationCompat.Builder notific = new NotificationCompat.Builder(context);
notific.setContentTitle(context.getString(R.string.notify_title));
notific.setContentText(content[1]);
notific.setSmallIcon(R.drawable.ic_notify);
notific.setAutoCancel(false);
notific.setPriority(NotificationCompat.PRIORITY_HIGH);
notific.setTicker(content[1]);
notific.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
Intent resultIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
notific.setContentIntent(resultPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(lastid, notific.build());
}
}
}
}
}
MainActivity onCreate:
[ loading shared preferences, setting onclicklisteners ]
angebot = new Angebot(this, getApplicationContext(), user, pin, getString(R.string.post_cmd_viewAngebot));
angebot.execute((Void) null);
Angebot is like lastID, but it uses the String to fill the TextViews.
EDIT 1:
Angebot:
public class Angebot extends ServerQuery {
protected Activity activity;
protected TextView datumView;
// more TextView s
protected Button hungerButton;
Angebot(Activity activity, Context context, String _user, String _pin, String _cmd) {
super(context, _user, _pin, _cmd);
this.activity = activity;
datumView = (TextView) this.activity.findViewById(R.id.datum);
// finding all other TextView s
}
#Override
protected void onPreExecute() {
showProgress(true);
}
#Override
protected void onPostExecute(final String success) {
Log.v(C.TAG_ALARMESSEN, "Angebot.onPostExecute");
showProgress(false);
if(success.equals("ERR") || success.length() < 1) {
Toast.makeText(activity.getApplicationContext(), context.getString(R.string.server_no_connection), Toast.LENGTH_LONG).show();
} else {
String content[] = success.split("(;)");
// put each content string in a TextView
} else {
Toast.makeText(context, context.getString(R.string.err02s), Toast.LENGTH_LONG).show(); // nicht angemeldet
}
}
}
#Override
protected void onCancelled() {
showProgress(false);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void showProgress(final boolean show) {
final View progressView = activity.findViewById(R.id.login_progress);
progressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
}
EDIT 2: stack trace:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:196)
at android.os.Handler.sendMessageAtTime(Handler.java:473)
at android.os.Handler.sendMessageDelayed(Handler.java:446)
at android.os.Handler.sendMessage(Handler.java:383)
at android.os.Message.sendToTarget(Message.java:363)
at android.os.AsyncTask.postResult(AsyncTask.java:300)
at android.os.AsyncTask.access$400(AsyncTask.java:156)
at android.os.AsyncTask$2.call(AsyncTask.java:264)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
I would assume that the workarounds for the AsyncTask bug mentioned here: onPostExecute not being called in AsyncTask (Handler runtime exception) would work in this case too but according to the OP they don't.
Instead of fixing the issue I would suggest to use an AsyncTaskLoader instead. There's plenty of articles about the difference (mainly the advantages) of AsyncTaskLoader compared to AsyncTask. Here are just two (good) examples):
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html and http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
The main advantages of an AsyncTaskLoader is that they are synced with the life cycle of the Activity or Fragment that started it and that they can deal with configuration changes gracefully (unlike AsyncTasks).
Here's how you'd implement it. First you need the AsyncLoaderClass:
class ServerQuery extends AsyncTaskLoader<String> {
String mResult;
public ServerQuery(Context context) {
super(context);
}
#Override
protected void onStartLoading() {
if (mResult == null) {
forceLoad(); // no data yet -> force load it
}
else {
deliverResult(mResult); // already got the data -> deliver it
}
}
#Override
public String loadInBackground() {
// load your data...
return mResult; // return the loaded data
}
#Override
public void cancelLoadInBackground() {
// do whatever it takes to stop loading
}
}
Replace the "// load your data..." part with whatever you have in your doInBackground method. That's all there is to the actual AsyncTaskLoader. As you can see there's no equivalent to onPostExecute and that's the beauty of the AsyncTaskLoader, it's there to load data, nothing else. All the issues that occurred because AsyncTask tried to update the ui on the onPostExecute are gone.
The logic in onPostExecute now resides completely in the Activity/Fragment that starts the loader. Here's how you do it:
LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(0, null, this);
this stands for LoaderManager.LoaderCallbacks meaning you have to implement that interface in your fragment/activity like so:
#Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new ServerQuery(this);
}
#Override public void onLoaderReset(Loader<String> loader) {}
#Override
public void onLoadFinished(Loader<String> loader, String data) {
// here goes your code to update the ui (previously onPostExecute);
}
onCreateLoader simply creates your loader (ServerQuery in this case) which will be managed by the LoaderManager henceforth. onLoadFinished will be called when the data is delivered to your fragment/activity allowing you to update the ui. You can check out one of my other answers to get some more information about Loaders that might help you understand how they work: https://stackoverflow.com/a/20916507/534471. Especially the part about configuration changes might be helpful.
Related
i am making an android socket app to communicate with the server for creating accounts, and i noticed i have to do this in AsyncTask sub class, even when i seperate it to another class without UI,but i am terribly confused how can i use AsyncTask on this, is there any one expert here who can help me please?
this is the code:
public class AccountCreator extends Activity {
public AccountCreator(){
super();
}
// for I/O
ObjectInputStream sInput; // to read from the socket
ObjectOutputStream sOutput; // to write on the socket
Socket socket;
public static String LOGTAG="Lifemate";
public String server = "localhost";
public String username = "user";
public String password = "rezapassword" ;
public int port = 1400;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(LOGTAG,"oncreate called");
this.start();
}
AccountCreator(String server, int port, String username,String password) {
this.server = "localhost";
this.port = 1400;
this.username = username;
Log.i(LOGTAG,"first accountcreator called");
}
public boolean start() {
// try to connect to the server
//this method returns a value of true or false when called
try {
socket = new Socket(server, port);
}
// if it failed not much I can so
catch(Exception ec) {
// display("Error connectiong to server:" + ec);
Log.i(LOGTAG,"Error connectiong to server:" + ec);
return false;
}
String msg = "Connection accepted " + socket.getInetAddress() + ":" +
socket.getPort();
// display(msg);
Log.i(LOGTAG, msg);
/* Creating both Data Stream */
try
{
sInput = new ObjectInputStream(socket.getInputStream());
sOutput = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException eIO) {
// display("Exception creating new Input/output Streams: " + eIO);
Log.i(LOGTAG,"Exception creating new Input/output Streams: " +
eIO);
return false;
}
// creates the Thread to listen from the server
// Send our username to the server this is the only message that we
// will send as a String. All other messages will be ChatMessage objects
try
{
sOutput.writeObject(username);
sOutput.writeObject(password);
}
catch (IOException eIO) {
// display("Exception doing login : " + eIO);
Log.i(LOGTAG,"Exception doing login : " + eIO);
disconnect();
return false;
}
// success we inform the caller that it worked
return true;
}
// private void display(String msg) {
// TextView screenprint = (TextView)findViewById(R.id.systemmessages);
// screenprint.setText(msg);
// }
private void disconnect() {
Log.i(LOGTAG,"reached disconnect");
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {} // not much else I can do
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {} // not much else I can do
try{
if(socket != null) socket.close();
}
catch(Exception e) {} // not much else I can do
}
public void Begin() {
Log.i(LOGTAG,"it begun");
int portNumber = 1400;
String serverAddress = server;
String userName = username;
String newpassword = password;
AccountCreator accountcreator = new AccountCreator(serverAddress, portNumber,
userName,password);
if(!accountcreator.start())
return;
}
}
i was trying to put whole code in Async, i dont know if was i right, do i need to do that also or just some parts of it?
In brief, AsyncTask contains a few methods which may be helpful:
onPreExecute:
This method is the first block of code executed when calling asyncTask.execute(); (runs on mainUIThread).
doInBackground:
Here you put all the code which may suspend you main UI (causes hang for your application) like internet requests, or any processing which may take a lot of memory and processing. (runs on background thread), contains one parameter taken from the asyncTask.execute(ParameterType parameter);
onPostExecute
Runs after doInBackground(). Its parameter is the return value of the doInBackground function, and mainly you put the changes in UI need to be done after the connection is finished (runs on mainUIThread)
You have to declare another class within the class you have already created.
class SomeName extends Async<Void, String, Void>{
protected void OnPreExecute(){
// starts the task runs on main UI thread
// Can be used to start a progress dialog to show the user progress
}
#Override
protected Void doInBackground(Void... params){
// does what you want it to do in the background
// Connected to the server to check a log-in
return result;
}
protected void OnPostExecute(Void result){
// finishes the task and can provide information back on the main UI thread
// this would be were you would dismiss the progress dialog
}
}
I'm trying to execute AsyncTask but when AsyncTask start and doInBackground finish (value returned), it is skipping the OnPostExecute and running the code requestTask2.execute() below, before i change the value in OnPostExecute, it is trying to run if condition so i'm getting null.
Let me explain with the code :
public void onClick(DialogInterface dialog,int id) {
Intent gt = new Intent(MainActivity.this, favorite.class);
String password = userInput.getText().toString();
String kadi = userInput2.getText().toString();
RequestTask2 requestTask2 = new RequestTask2();
requestTask2.execute("http://www.example.com/androfav/?fav2="+kadi+":"+password).get();
if (asd2[0][0]!=null && asd2[1][0]!=null ) {
// This if condition works before on Post Excecute and it is causing the problem.
if (asd2[0][0].equals(password) && asd2[1][0].endsWith(kadi) ) {
// Codes
}}
class RequestTask2 extends AsyncTask<String, String, String> {
private ProgressDialog dialog = new ProgressDialog(MainActivity.this);
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
dialog.setMessage("Diziler Yükleniyor \n Lütfen Bekleyin...");
dialog.show();
dialog.setCancelable(false);
}
#Override
protected String doInBackground(String... uri2) {
HttpClient httpclient2 = new DefaultHttpClient();
HttpResponse response2;
String responseString2 = null;
try {
response2 = httpclient2.execute(new HttpGet(uri2[0]));
StatusLine statusLine2 = response2.getStatusLine();
if (statusLine2.getStatusCode() == HttpStatus.SC_OK) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
response2.getEntity().writeTo(out);
out.close();
responseString2 = out.toString();
} else {
// Closes the connection.
response2.getEntity().getContent().close();
throw new IOException(statusLine2.getReasonPhrase());
}
} catch (ClientProtocolException e) {
// TODO Handle problems..
} catch (IOException e) {
// TODO Handle problems..
}
return responseString2;
}
#Override
protected void onPostExecute(String result2) {
super.onPostExecute(result2);
try {
JSONArray jsonResponse2 = new JSONArray(result2);
asd2 = new String[3][jsonResponse2.length()];
//............................... Codes
dialog.dismiss();
}
}
How can i wait for OnPostExecute before the if condition works.
Hope i could understand myself.
Thanks in advance.
AsyncTask as the name suggests is Asynchronous. You need to move the if condition to onPostExecute.
Move the below to onPostExecute
JSONArray jsonResponse2 = new JSONArray(result2);
asd2 = new String[3][jsonResponse2.length()];
if (asd2[0][0]!=null && asd2[1][0]!=null ) {
if (asd2[0][0].equals(password) && asd2[1][0].endsWith(kadi) ) {
// Codes
}
}
Edit:
I din't notice you called get(). Calling get() makes Asynctask no more asynchronous. You should never call get() just execute() is enough.
Why do you need to call get() which blocks the ui thread waiting for the task to be finished.
You should always avoid calling get() when using AsyncTask. Instead, do all of your post-processing in onPostExecute
#Override
protected void onPostExecute(String result2) {
super.onPostExecute(result2);
try {
JSONArray jsonResponse2 = new JSONArray(result2);
asd2 = new String[3][jsonResponse2.length()];
if (asd2[0][0]!=null && asd2[1][0]!=null ) {
if (asd2[0][0].equals(password) && asd2[1][0].endsWith(kadi) ) {
// Codes
}
}
}
dialog.dismiss();
}
I am trying to use ProgressDialog. when i run my app the Progress Dialog box show and disappear after 1 second. I want to show it on completion of my process.. Here is my code:
public class MainActivity extends Activity {
android.view.View.OnClickListener mSearchListenerListener;
private ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new YourCustomAsyncTask().execute(new String[] {null, null});
}
private class YourCustomAsyncTask extends AsyncTask <String, Void, Void> {
protected void onPreExecute() {
dialog = new ProgressDialog(MainActivity.this);
dialog.setMessage("Loading....");
dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show(); //Maybe you should call it in ruinOnUIThread in doInBackGround as suggested from a previous answer
}
protected void doInBackground(String strings) {
try {
// search(strings[0], string[1]);
runOnUiThread(new Runnable() {
public void run() {
// updateMapWithResult(); //Or call it onPostExecute before progressDialog's dismiss. I believe this method updates the UI so it should run on UI thread
}
});
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
dialog.dismiss();
//result
}
#Override
protected Void doInBackground(String... params) {
// TODO Auto-generated method stub
return null;
}
}
}
Updated Question:
#Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
Log.i("PATH",""+mDatabase.getPath());
mDatabase.execSQL(FTS_TABLE_CREATE);
loadDictionary();
}
/**
* Starts a thread to load the database table with words
*/
private void loadDictionary() {
new Thread(new Runnable() {
public void run() {
try {
loadWords();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private void loadWords() throws IOException {
Log.d(TAG, "Loading words...");
for(int i=0;i<=25;i++)
{ //***//
final Resources resources = mHelperContext.getResources();
InputStream inputStream = resources.openRawResource(raw_textFiles[i]);
//InputStream inputStream = resources.openRawResource(R.raw.definitions);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
StringBuilder sb = new StringBuilder();
while ((word = reader.readLine()) != null)
{
sb.append(word);
// Log.i("WORD in Parser", ""+word);
}
String contents = sb.toString();
StringTokenizer st = new StringTokenizer(contents, "||");
while (st.hasMoreElements()) {
String row = st.nextElement().toString();
String title = row.substring(0, row.indexOf("$$$"));
String desc = row.substring(row.indexOf("$$$") + 3);
// Log.i("Strings in Database",""+title+""+desc);
long id = addWord(title,desc);
if (id < 0) {
Log.e(TAG, "unable to add word: " + title);
}
}
} finally {
reader.close();
}
}
Log.d(TAG, "DONE loading words.");
}
I want to show ProgressDialogue box untill all words are not entered in the database. This code is in inner calss which extends SQLITEHELPER. so how to can i use ProgressDialogue in that inner class and run my addWords() method in background.
You cannot have this
runOnUiThread(new Runnable() {
public void run() {
// updateMapWithResult(); //Or call it onPostExecute before progressDialog's dismiss. I believe this method updates the UI so it should run on UI thread
}
});
in your doInBackground().
Progress dialog doesn't take priority when there is some other action being performed on the main UI thread. They are intended only when the actions are done in the background. runonUIthread inside doInBackground will not help you. And this is normal behavior for the progressdialog to be visible only for few seconds.
You have two doInBackground() methods inside your AsyncTask Class. Remove the runOnUiThread() from First doInBackground() and move it to second doInBackground() which has #Override annotation.
I don't know whether you wantedly written two doInBackground() methods or by mistake but it is not good to have such confusion between the Method. Your AsyncTask is not calling the first doInBackground() and it will call doInBackground() which has #Override annotation. So your ProgressDialog is dismissed in 1 second of time as it returns null immediately.
this is the class that calls my Service:
public class TicketList extends ListActivity
{
private ArrayList<Tickets> alTickets = new ArrayList<Tickets>();
private boolean listCreated = false;
private static Drawable background = null;
private Resources res;
private Tickets ticket = null;
private TicketConnector localService;
/**
* Called when the activity is first created.
*
*/
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.ticketlist);
if(!listCreated)
{
connectService();
//populateList();
res = getResources();
background = res.getDrawable(R.drawable.background);
listCreated = true;
}
TicketAdapter StatisticsAdapter = new TicketAdapter(this, alTickets);
setListAdapter(StatisticsAdapter);
}
/**
* Populates the ListView.
* This needs to be done once the Activity is created and if the menu entry refresh is hit.
*/
private void populateList()
{
try
{
String jsonString = localService.queryData(new String[] {"getTicketList"}, new String[] {"Offen"});
//String jsonString = new TicketConnector().queryData(new String[] {"getTicketList"}, new String[] {"Offen"});
JSONObject jsonObj = new JSONObject(jsonString);
JSONArray ticketArray = jsonObj.getJSONArray("tickets");
Tickets[] tickets = new Tickets[ticketArray.length()];
for (int i=0;i<ticketArray.length();i++)
{
JSONObject object = ticketArray.getJSONObject(i).getJSONObject("ticket");
ticket = new Tickets(object.getString("id"), object.getString("color"), object.getString("priority"));
alTickets.add(ticket);
}
}
catch (Exception e)
{
Log.e("DayTrader", "Exception getting JSON data", e);
}
}
private void connectService()
{
Intent intent = new Intent(getApplicationContext(), TicketConnector.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
public void getData()
{
String s = localService.queryData(new String[] {"getTicketList"}, new String[] {"Offen"});
}
ServiceConnection connection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder binder)
{
Toast.makeText(TicketList.this, "Service connected",Toast.LENGTH_SHORT).show();
localService = ((TicketConnector.LocalBinder)binder).getService();
Log.i("INFO", "Service bound: TicketConnector");
}
#Override
public void onServiceDisconnected(ComponentName name)
{
Toast.makeText(TicketList.this, "Service disconnected",Toast.LENGTH_SHORT).show();
localService = null;
Log.i("INFO", "Service unbound: TicketConnector");
}
};
}
And this is the service:
public class TicketConnector extends Service
{
private SharedPreferences settings = null;
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder binder = new LocalBinder();
private String username = null;
private String password = null;
private String server = null;
private String port = null;
private String urlStr = null;
private String result = null;
#Override
public void onCreate()
{
settings = CMDBSettings.getSettings(this);
username = settings.getString("username", "");
password = settings.getString("password", "");
server = settings.getString("server", "");
port = settings.getString("serverport", "");
}
#Override
public IBinder onBind(Intent intent)
{
return binder;
}
#Override
public void onDestroy()
{
}
public String queryData(String[] actions, String[] category)
{
//http://localhost:8080/MobileCMDB/TicketListener?format=json&actions=getTicketList&ticketcategory=Open
urlStr = "http://"+server+":"+port+"/MobileCMDB/TicketListener?format=";
new jsonParser().execute(actions);
return result;
}
abstract class BaseParser extends AsyncTask<String, Integer, String>
{
protected BaseParser(String format)
{
urlStr += format;
}
private String makeUrlString(String[] actions, String[] category)
{
StringBuilder sb = new StringBuilder(urlStr);
for (int i=0;i<actions.length;i++)
{
sb.append("&actions=");
sb.append(actions[i]);
sb.append("&ticketcategory=");
sb.append(category[i]);
}
return sb.toString();
}
protected InputStream getData(String[] actions, String[] category) throws Exception
{
URI uri = new URI(makeUrlString(actions, category));
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(uri);
request.addHeader("Accept-Encoding","gzip");
HttpResponse response = client.execute(request);
InputStream content = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip"))
{
content = new GZIPInputStream(content);
}
return content;
}
#Override
protected void onPostExecute(String jsonString)
{
result = jsonString;
}
}
private class jsonParser extends BaseParser
{
public jsonParser()
{
super("json");
}
#Override
protected String doInBackground(String... actions)
{
String[] category = new String[] {"Open"};
StringBuilder json = null;
try
{
json = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(getData(actions, category)));
String line = reader.readLine();
while (line != null)
{
json.append(line);
line = reader.readLine();
}
}
catch (Exception e)
{
Log.e("PrimeCMDB - Network", "Exception getting JSON data", e);
}
return json.toString();
}
}
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class LocalBinder extends Binder
{
public TicketConnector getService()
{
return TicketConnector.this;
}
}
}
This are the two activities in the AndroidManifest.xml:
<activity
android:name=".ticket.TicketList"
android:label="#string/ticket"
/>
<service
android:name=".network.TicketConnector"
android:enabled="true"
/>
onServiceConnected is never executed. Did I miss something?
Here is the output of LogCat at verbose mode while activating the TicketList Activity:
09-28 23:22:11.420: INFO/ActivityManager(795): Starting activity: Intent { cmp=org.mw88.cmdb/.gui.TicketListActivity }
09-28 23:22:12.340: WARN/ActivityManager(795): Binding with unknown activity: android.os.BinderProxy#4410bf30
09-28 23:22:16.090: INFO/ActivityManager(795): Displayed activity org.mw88.cmdb/.gui.TicketListActivity: 4606 ms (total 4606 ms)
Thank you all for your Answers.
I found the question after searching Google for this log message:
Binding with unknown activity: android.os.BinderProxy
It seems that Android has a bug when using bindService to fill a TabSpec Activity!
The solution was pretty simple:
just replace bindService with getApplicationContext().bindService
Now it works perfectly ;-)
I don't think that it is a bug.
In my opinion, that's because when you use the TabActivity, the child activities will be embedded in the parent (TabActivity) as more like a view with activity behavior, hence its context cannot serve as an actual context.
So for the workaround, you need to get and use the parent (using getParent()) or the application context (using getApplicationContext()) which can act as an "actual" context.
But again, this is just my opinion because I cannot provide any link to any documentation related to this one. :)
I'd like to set a ListView to data I get from a web service. I get the data in a AsyncTask instance, but when I try to set some of my ListView attributes, it crashes (on line "lv.setVisibility(View.VISIBLE);"). Anybody can help?
thanks
public class Atable extends Activity {
private EditText mSearch;
private static final int ACTIVITY_EDIT=0;
private Button mSearchButton;
private TextView mNoresults;
private ListView lv;
private CheckBox checkBox;
private LocationManager locationManager;
private RestaurantList restaurantList;
private Criteria criteria;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv= (ListView)findViewById(R.id.listview);
mNoresults = (TextView) findViewById(R.id.noresults);
mNoresults.setVisibility(View.GONE);
mSearchButton = (Button)this.findViewById(R.id.button);
checkBox = (CheckBox) findViewById(R.id.local_check);
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
mSearchButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mNoresults.setVisibility(View.GONE);
mSearch = (EditText) findViewById(R.id.search);
String tmp_str = mSearch.getText().toString().replace(" ","+");
String url = "http://www.atable.org/getRestaurantByQuery/?query=" + tmp_str;
if (checkBox.isChecked()) {
//get location
String provider = locationManager.getBestProvider(criteria, true);
Location location = locationManager.getLastKnownLocation(provider);
if (location!=null) {
String lat = String.valueOf(location.getLatitude());
String lng = String.valueOf(location.getLongitude());
url += "&lat="+lat+"&lng="+lng;
}
}
new GetRestaurantData().execute(url);
}
});
};
private class GetRestaurantData extends AsyncTask<String, Boolean, RestaurantList> {
private HttpClient httpclient = new DefaultHttpClient();
#Override
protected RestaurantList doInBackground(String... url) {
publishProgress(true);
HttpGet httpget = new HttpGet(url[0]);
// Execute the request
HttpResponse response;
try {
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
Reader r = new InputStreamReader(instream);
Gson gson = new Gson();
restaurantList = gson.fromJson(r, RestaurantList.class);
int nResults = restaurantList.getSize();
if (nResults>0) {
lv.setVisibility(View.VISIBLE); //app crashes here
lv.setAdapter( new ArrayAdapter<String>(Atable.this ,android.R.layout.simple_list_item_1,restaurantList.getRestaurantNames()));
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Atable.this, RestaurantDescription.class);
Restaurant tmp_resto = restaurantList.getRestaurant((int)id);
String tmp_categories = tmp_resto.getCategories().get(0);
for (int i=1; i<tmp_resto.getCategories().size(); i++) {
tmp_categories+=", "+tmp_resto.getCategories().get(i);
}
String address = tmp_resto.getStreet()+", "+tmp_resto.getStreetNumber()+"\n"+tmp_resto.getCity()+
" "+tmp_resto.getPostalCode()+"\n"+tmp_resto.getCountry();
intent.putExtra("name", tmp_resto.getName());
intent.putExtra("address", address);
intent.putExtra("rating", tmp_resto.getRating());
intent.putExtra("price_range", tmp_resto.getPriceRange());
intent.putExtra("categories", tmp_categories);
intent.putExtra("latitude", tmp_resto.getLatitude());
intent.putExtra("longitude", tmp_resto.getLongitude());
startActivityForResult(intent, ACTIVITY_EDIT);
}
});
}
else {
lv.setVisibility(View.GONE);
mNoresults.setVisibility(View.VISIBLE);
}
//Closing the input stream will trigger connection release
instream.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return restaurantList;
}
#Override
protected void onProgressUpdate(Boolean... progress) {
// line below coupled with
// getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS)
// before setContentView
// will show the wait animation on the top-right corner
Atable.this.setProgressBarIndeterminateVisibility(progress[0]);
}
#Override
protected void onPostExecute(RestaurantList result) {
publishProgress(false);
// Do something with result in your activity
}
}
In Android, all UI updates are done on the main thread (also called UI thread). It's good when you spawn a new thread to do time consuming tasks in order not to block the UI.
But in order to avoid all sorts of indeterminate behaviors, UI updates always have to be done on the UI thread. If you ever attempt to do so from a different thread, an exception will be thrown.
In your example, I see you're performing different updatings to ListView based on the value of nResults. You can try returning nResults for doInBackground(). It will be passed to onPostExecute(), which will be executed on the UI thread.
You wanna check out this article for some useful information about threadings in Android: http://android-developers.blogspot.com/2009/05/painless-threading.html
HTH
You can't access the ui thread from the doinbachground method.. you need to do that from the post execute, pre execute or on progress update methods...