I have spent last couple of hours searching for answers here but nothing seems to make it clear for me.
Here's my problem: I have a simple app with a main menu. One of the options retrieves a list of comments from a PHP server, updates the database and then displays a ListView.
Everything functions properly inside. Now, every time I press back and then start the activity again, a new thread is started. I managed to get to more than 50+ waiting threads. I'm using the DDMS tab from Eclipse to monitor the treads.
If I try to call Looper.getLooper().quit() or Looper.myLooper().quit() I get an error saying "main thread not allowed to quit".
What am I doing wrong and where/how should I be stopping the thread?
Here's the code:
public class RequestActivity extends Activity {
ProgressDialog pDialog;
DatabaseHelper db;
int userId;
myCursorAdapter adapter;
Thread runner = null;
ListView list;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
userId = myApplication.getInstance().getSession().getUserId();
setContentView(R.layout.request_list);
db = new myProvider.DatabaseHelper(this);
Cursor cursor = db.getRequests(userId);
startManagingCursor(cursor);
adapter = new myCursorAdapter(RequestActivity.this, cursor);
list = (ListView) findViewById(R.id.list);
list.setVisibility(View.INVISIBLE);
list.setAdapter(adapter);
pDialog = ProgressDialog.show(RequestActivity.this, "", "Loading ...", true, false);
runner = new Thread() {
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
adapter.getCursor().requery();
}
};
public void run() {
Looper.prepare();
// ... setup HttpGet
try {
// ... get HTTP GET response
if (response != null) {
// ... update database
handler.sendEmptyMessage(0);
}
} catch(Exception e) {
// .. log exception
}
Looper.loop();
}
};
runner.start();
}
private static class ViewHolder {
// .. view objects
}
private class myCursorAdapter extends CursorAdapter {
private LayoutInflater inflater;
public myCursorAdapter(Context context, Cursor c) {
super(context, c);
inflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// .. bind data
if (cursor.isLast()) {
pDialog.dismiss();
list.setVisibility(View.VISIBLE);
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = inflater.inflate(R.layout.request_list_item, parent, false);
ViewHolder holder = new ViewHolder();
// .. set holder View objects
view.setTag(holder);
return view;
}
}
}
You're calling quit() on your main thread's Looper. Not the thread's Looper.
You can try this. Create a separate private inner-class that extends Thread. In the run() method, do a call to Looper.myLooper() to save the thread's Looper instance in the class. Then have a quitting method that calls quit on that looper.
Example:
private class LooperThread extends Thread{
private Looper threadLooper;
#Override
public void run(){
Looper.prepare();
threadLooper = Looper.myLooper();
Looper.loop();
}
public void stopLooper(){
if(threadLooper != null)
threadLooper.quit();
}
}
On your onDestroy method do you make any calls to stop the thread?
public void onDestroy()
{
Thread moribund = runner;
runner = null
moribund.interrupt();
}
You are calling the Looper.quit() method within your main (UI) thread, which is not allowed. Try posting a Runnable to the handler inside your thread that calls Looper.quit(). This will cause the Looper within the thread context to quit.
Related
I have an adapter used to display messages on the list view alike messages in chat application . I am able to display the content flawlessly once the activity is created , but when I go back and create activity again , adapter don't work as usual .
What I found in debugging is follows:
function receives() is called when message is received and update the
register , as I mentioned above there is no problem to display the
data in list view once the activity is created , but once I go back
and relauch the activity I am not able to display received messages .
Is there something I am missing in onResume() onPause or onStart() method with respect to custom adapter such as registering or decalring the custom adapter again? Thanks for help.
Following is the code of my activity class which uses custom adapter to display sent and received messages:
public class hotListener extends ListActivity {
private XMPPConnection connection;
private IBinder binder;
private Handler mHandler = new Handler();
private ArrayList<String> messages = new ArrayList<String>();
ArrayList<ChatMessage> messagex= new ArrayList<ChatMessage>();;
ChattingAdapter adaptex;
Intent mIntent ;
private ListView listview;
EditText sender_message ;
String msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listener);
//messagex.add(new ChatMessage("Hello", false));
adaptex = new ChattingAdapter(getApplicationContext(),messagex);
setListAdapter(adaptex);
Button send_button = (Button) findViewById(R.id.chat_send_message);
sender_message = (EditText) findViewById(R.id.chat_input);
send_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
msg = sender_message.getText().toString();
sender_message.setText("");
if(!(msg.length()==0)){
messagex.add(new ChatMessage(msg, true));
//addNewMessage(new ChatMessage(msg, true));
adaptex.notifyDataSetChanged();
getListView().setSelection(messagex.size()-1);
}
}
});
if(!isMyServiceRunning()){
System.out.println("seems like service not running");
startService(new Intent(this,xService.class));
System.out.print(" now started ");
}
}
#Override
protected void onStart(){
super.onStart();
Boolean kuch = bindService(new Intent(this,xService.class), mConnection,Context.BIND_AUTO_CREATE);
//System.out.println(kuch);
System.out.println("bind done");
}
private void receives(XMPPConnection connection2) {
//ChatManager chatmanager = connection.getChatManager();
connection2.getChatManager().addChatListener(new ChatManagerListener() {
#Override
public void chatCreated(Chat arg0, boolean arg1) {
arg0.addMessageListener(new MessageListener() {
#Override
public void processMessage(Chat chat, Message message) {
final String from = message.getFrom();
final String body = message.getBody();
mHandler.post(new Runnable() {
ChatMessage kudi = new ChatMessage(body, false);
#Override
public void run() {
messagex.add(kudi);
adaptex.notifyDataSetChanged();
getListView().setSelection(messagex.size()-1);
Toast.makeText(hotListener.this,body,Toast.LENGTH_SHORT).show(); }
});
}
});
}
});
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for(RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)){
if(xService.class.getName().equals(service.service.getClassName())){
return true;
}
}
//System.out.print("false");
return false;
}
#Override
protected void onResume() {
bindService(new Intent(this, xService.class), mConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
#Override
protected void onPause() {
unbindService(mConnection);
super.onPause();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
connection = null;
service = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//System.out.println("binding in hot listener");
service = ((xService.MyBinder)binder).getService();
connection = service.getConnection();
receives(connection);
Log.wtf("Service","connected");
}
};
void addNewMessage(ChatMessage m)
{
System.out.println("1");
messagex.add(m);
System.out.println("2");
adaptex.notifyDataSetChanged();
System.out.println("3");
getListView().setSelection(messagex.size()-1);
}
}
Here is my custom adapter (there is no problem in custom adapter but adding to make things clear) :
public class ChattingAdapter extends BaseAdapter{
private Context mContext;
private ArrayList<ChatMessage> mMessages;
public ChattingAdapter(Context context, ArrayList<ChatMessage> messages) {
super();
this.mContext = context;
this.mMessages = messages;
}
#Override
public int getCount() {
return mMessages.size();
}
#Override
public Object getItem(int position) {
return mMessages.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ChatMessage message = (ChatMessage) this.getItem(position);
ViewHolder holder;
if(convertView == null)
{
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.listitem, parent, false);
holder.message = (TextView) convertView.findViewById(R.id.text1);
convertView.setTag(holder);
}
else
holder = (ViewHolder) convertView.getTag();
holder.message.setText(message.getMessage());
LayoutParams lp = (LayoutParams) holder.message.getLayoutParams();
//Check whether message is mine to show green background and align to right
if(message.isMine())
{ holder.message.setBackgroundResource(R.drawable.msgbox_new_selected_go_up);
lp.gravity = Gravity.RIGHT;
}
//If not mine then it is from sender to show orange background and align to left
else
{
holder.message.setBackgroundResource(R.drawable.msgbox_other_go_up);
lp.gravity = Gravity.LEFT;
}
holder.message.setLayoutParams(lp);
//holder.message.setTextColor(R.color.textColor);
return convertView;
}
private static class ViewHolder
{
TextView message;
}
#Override
public long getItemId(int position) {
//Unimplemented, because we aren't using Sqlite.
return position;
}
}
p.s: I am not storing any messages in sqlite as I dont want to restore messages for now, but I want new messages to be displayed at least onresume of activty. I can display sent messages after pressing send button but no received messages which works fine for the first time activity is created.
EDIT: I did more debugging , it turns out problem is not in resume activity , if I dont use receives() function for first time , and resume activity after going back , then receives() will work , that means , function inside receives() : getListView().setSelection(messagex.size()-1); works only once .
Either first time on receiving message or next time if and only if its not called first time on activity .
I think problem lies when you try to resume activity , you are still running the previous mHandler running and thus your instance of message is not destroyed and when you resume your activity it creates a problem . Make sure your mhandler destroys all instance of objects when unstop is called.
There's no place in your code where you save your messagex ArrayList. When you quit your activity by hitting back, your array get's distroyed (Garbage Collection takes care of it).
When you relaunch your activity your messagex ArrayList is created again, it's a brand new variable.
In fact, you're not relaunching your activity, you're creating a new instance.
EDIT:
I've never worked with the XMPPConnection objects before, but something else worth trying is the following:
When binding to the service, you're calling connection2.getChatManager().addChatListener and also arg0.addMessageListener but when unbinding you're not calling any removeXXX methods. I could be that since you're not removing your listeners, the whole XMPPConnection object still have references to the listeners that live in a dead Activity, and they are not being garbage collected.
I have a web browser application.
A AutoCompleteTextView act as a Url textbox, and being fetch a list (Cursor Type) when application start.
Below code is work well, but i don't know it correct to use AsyncTask or not.
So, did the AutoCompleteTextView will freeze will application launch?
And i monitor the thread via Eclipse thread monitor, the AsyncTask#1 thread keep in wait status when done.So how can i close the thread???
Code:
public class BrowserActivity extends StandOutWindow {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Build the layout
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.activity_browser, frame, true);
AutoCompleteTextView txtUrl = (AutoCompleteTextView) v.findViewById(R.id.txtUrl);
//Run the async task
BrowserDataTask bdTask = new BrowserDataTask();
bdTask.execute(txtUrl);
}
//A method belong to BrowserActivity class and reusable.
public Cursor getBrowserData() {
String[] projection = new String[] { "_id", Browser.BookmarkColumns.TITLE,
Browser.BookmarkColumns.URL };
Cursor mCur = getContentResolver().query(android.provider.Browser.BOOKMARKS_URI,
projection, null, null, null);
return mCur;
}
//Sub Class of BrowserActivity
private class BrowserDataTask extends AsyncTask<AutoCompleteTextView, Integer, Cursor>
{
private AutoCompleteTextView m_acText;
#Override
protected Cursor doInBackground(AutoCompleteTextView...params) {
m_acText = params[0];
return getBrowserData();
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
}
#Override
protected void onPostExecute(Cursor result) {
//UrlAdapter, custome Cursor Adapter from other class.
UrlAdapter adapter = new UrlAdapter(BrowserActivity.this, result);
m_acText.setAdapter(adapter);
}
}
}
Why is it waiting?
AyncTask uses ThreadPoolExecutor and hence you they might not get destroyed but rather kept, because destroying and reinitializing them would be a definite waste. In case you really want to close it, call cancel() on it, that might help.
Will it freeze the app?
No, it won't.
Also, you are never setting your progress!
I am having a progress dialog for a process. But i am taking a null pointer exception in my thread. But, when i remove the progress dialog. I am no longer taking an exception.
My code is as this
public class PlayedActivity extends ListActivity {
private PullToRefreshListView listView;
final Context context = this;
public Handler handler;
Runnable sendNumbers2;
List<On> playedOn;
DatabaseHandlerOn db;
private ProgressDialog m_ProgressDialog;
private ArrayList<On> m_results = null;
private PlayedOnAdapter m_adapter;
#SuppressLint({ "HandlerLeak", "HandlerLeak" })
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playedonnumara);
db = new DatabaseHandlerOnNumara(getApplicationContext());
m_results = new ArrayList<OnNumara>();
this.m_adapter = new PlayedOnNumaraAdapter(this, R.layout.playedrowon, m_results);
this.setListAdapter(this.m_adapter);
sendNumbers2 = new Runnable() {
#Override
public void run() {
playedOn = db.getAllContacts();
for (On on : playedOn) {
m_results.add(on);
}
Collections.reverse(m_results);
//m_ProgressDialog.dismiss();
handler.sendEmptyMessage(0);
}
};
Thread thread = new Thread(sendNumbers2,"sendNumbers2");
thread.start();
/*m_ProgressDialog = ProgressDialog.show(PlayedOnNumaraActivity.this,
"",getString(R.string.PleaseWait), true);
m_ProgressDialog.setCancelable(true);
*/
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
m_adapter.notifyDataSetChanged();
}
};
}
}
}
The code above is working and takes no exception when progress dialog codes are commented
Without your LogCat logs, I can only guess.
m_ProgressDialog is defined after you start your thread. Why? Define it before the thread is started.
Also, I would recommend an AsyncTask for this, instead. See Painless Threading for details on that.
In my Main Activity, I have a Thread that is doing alot of stuff, including adding some records to a database. In my second activity, which inherit from the Main Activity, I want to do a query to my database. But I need to check if the first thread in the Main Activity is finished, what I've done so far is:
public class History extends Main {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(MainThread.isAlive())) {
getFromDatabase();
}
}
}
This is my getFromDatabase() method
pd = ProgressDialog.show(this, "Please Wait",
"Getting cases from database", false);
Thread t = new Thread(this);
t.start();
which will call this run method:
#Override
public void run() {
ArrayList<Case> c = db1.getAllCases();
Message msg = handler.obtainMessage();
msg.obj = c;
handler.sendMessage(msg);
}
private Handler handler = new Handler() {
#SuppressWarnings("unchecked")
#Override
public void handleMessage(Message m) {
pd.dismiss();
list = (ArrayList<Case>) m.obj;
tempList = getCaseNumberToTempList(list);
tempCaseList = createTempList(list);
lv.setAdapter(new CustomAdapter(History.this, list));
lv.setTextFilterEnabled(true);
}
};
But if I do so, the following line of code will crash my application, it will give a NullPointerException:
if(!(MainThread.isAlive())) {
getFromDatabase();
}
How can I be sure that that the first thread is finished with all the work before I query the database from my history activity?
You can make the Thread in the getFromDatabase() method a static class level variable, write a static get method for it in your Activity, and check for isAlive() in your child Activity.
How about simply using a semaphore variable that you modify from the thread once it has reached a certain state?
I have a SQLite query returning thousands of rows, which I want to visualize using a ListView.
In order to keep my UI thread responsive, I create the ListAdapter on a background thread.
However the statement that takes most time (and can cause ANR) is ListActivity.setListAdapter which I have to execute on the UI thread... Any advice?
public class CursorTestActivity extends ListActivity {
private static final String LOGTAG = "DBTEST";
private DatabaseManager mDbManager;
private Cursor mCursor;
private HandlerThread mIOWorkerThread;
private Handler mIOHandler;
private Handler mUIHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDbManager = new DatabaseManager(this);
mUIHandler = new Handler();
createIOWorkerThread();
log("creating cursor");
mCursor = mDbManager.getCursor(); // does db.query(...)
startManagingCursor(mCursor);
mIOHandler.post(new Runnable() {
#Override
public void run() {
setMyListAdapter();
}
});
log("onCreate done");
}
private void setMyListAdapter() {
log("constructing adapter");
// CustomCursorAdapter implements bindView and newView
final CustomCursorAdapter listAdapter = new CustomCursorAdapter(this,
mCursor, false);
log("finished constructing adapter");
mUIHandler.post(new Runnable() {
#Override
public void run() {
log("setting list adapter");
setListAdapter(listAdapter); // gets slower the more rows are returned
log("setting content view");
setContentView(R.layout.main);
log("done setting content view");
}
});
}
private void createIOWorkerThread() {
mIOWorkerThread = new HandlerThread("io_thread");
mIOWorkerThread.start();
Looper looper = mIOWorkerThread.getLooper();
mIOHandler = new Handler(looper);
}
private void destroyIOWorkerThread() {
if (mIOWorkerThread == null)
return;
Looper looper = mIOWorkerThread.getLooper();
if (looper != null) {
looper.quit();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mDbManager != null)
mDbManager.close();
destroyIOWorkerThread();
}
private static void log(String s) {
Log.d(LOGTAG, s);
}
}
Cursors are lazy loaded therefore at first data access the cursor is loaded - getCount is such access. The setAdapter method is invoking getCount on cursor - there is the performance issue.
You can set empty cursor in adapter and display empty list during loading cursor. Then use changeCursor method in adapter to change cursor to the new one.
You can fetch at first for example 100 rows in the first query, then load more rows in the background and changeCursor to the new one.
Or you can create own implementation of Cursor that has own implementation of getCount and fetches demanded rows on request.
consider using PagedListAdapter. it's something similar as proposed by #pawelziemba but provided by Android Framework https://developer.android.com/reference/android/arch/paging/PagedListAdapter -- if you can use Room framework you can get that for free
load data async in ViewHolder - remember to cancel work if viewHolder gets rebinded and cache loaded data if needed