How to implement a callback in a Fragment starting a Thread? - android

I try to close a ProgressDialog via Callback from Thread to fragment, but I don't know which reference I need to pass.
Some where in my Fragment I do the following:
c_thread_connectToDevice = new c_Thread_ConnectToDevice(UserSelectedDevice,
sFinalDonglePassword, getActivity());
if(UserSelectedDevice != null){
c_thread_connectToDevice.start();
mProgessDialog.setTitle(R.string.ProgressDialog_Fragmentsetpassword_Title);
mProgessDialog.setMessage(getResources().getString(R.string.ProgressDialog_Fragmentsetpassword_Message));
mProgessDialog.setIndeterminate(true);
mProgessDialog.show();
The Callback is:
public void dismissProgressDialog(){
mProgessDialog.dismiss();
if(!c_thread_connectToDevice.isbConnectionState()){
tv_Fragmentsetpassword_userhint.setTextColor(getResources().getColor(R.color.Mercedes_RED, null));
tv_Fragmentsetpassword_userhint.setText(R.string.tv_Fragmentsetpassword_ConnectionFailed);
}else {
tv_Fragmentsetpassword_userhint.setText(R.string.tv_Fragmentsetpassword_ConnectionSucces);
tv_Fragmentsetpassword_userhint.setTextColor(getResources().getColor(R.color.Mercedes_GREEN, null));
}
}
In my Thread the I use the following Code:
private WeakReference<Activity> weakReference;
...
dismissProgressDialog();
...
private void dismissProgressDialog(){
Activity activity = weakReference.get();
activity.dismissProgressDialog();
}
I know this could not work. But what is the right thing to pass?

What #Zach Bublil told me, brought me to this solution.
private Handler handler = new Handler(Looper.getMainLooper());
c_thread_connectToDevice = new c_Thread_ConnectToDevice(UserSelectedDevice, sFinalDonglePassword, c_Fragment_RoutineStartConnection_setpassword.this);
if(UserSelectedDevice != null){
c_thread_connectToDevice.start();
mProgessDialog = new ProgressDialog(getContext());
mProgessDialog.setTitle(R.string.ProgressDialog_Fragmentsetpassword_Title);
mProgessDialog.setMessage(getResources().getString(R.string.ProgressDialog_Fragmentsetpassword_Message));
mProgessDialog.setIndeterminate(true);
mProgessDialog.show();
CallBack
public void dismissProgressDialog(){
mProgessDialog.dismiss();
handler.post(new Runnable() {
#Override
public void run() {
if(!c_thread_connectToDevice.isbConnectionState()){
tv_Fragmentsetpassword_userhint.setTextColor(getResources().getColor(R.color.Mercedes_RED, null));
tv_Fragmentsetpassword_userhint.setText(R.string.tv_Fragmentsetpassword_ConnectionFailed);
}else {
tv_Fragmentsetpassword_userhint.setText(R.string.tv_Fragmentsetpassword_ConnectionSucces);
tv_Fragmentsetpassword_userhint.setTextColor(getResources().getColor(R.color.Mercedes_GREEN, null));
}
}
});
InsideFragment
private c_Thread_ConnectedToBluetoothDevice c_thread_connectedToBluetoothDevice;
public c_Thread_ConnectToDevice(BluetoothDevice device, String sFinalDonglePassword, c_Fragment_RoutineStartConnection_setpassword reference) {
this.mBluetoothDevice = device;
this.sFinalDonglePassword =sFinalDonglePassword;
this.reference = reference;
}
...
dismissProgressDialog();
...
private void dismissProgressDialog(){
reference.dismissProgressDialog();
}
What is difficult for me to understand is, why I need to run the callback Text editions on mainthread. If I don't do that there is an exception to "Only the original thread creating the view..." but this is maybe caused by
tools:context=".c_RoutineStartConnection"
which I used in the Fragment layout for better usability.

Related

Getting thread result in another activity for Android

My Main Activity invokes ResultActivity and at the same time invokes a Runnable thread. I want that Result activity doesn't wait for some part of result processing (Image) which may be provided later by thread and UI may be updated accordingly.
What I've tried to do is given below.
MainActivity (below method is actually a callback from another thread):
#Override
public void onCreate(Context context) {
resultImageProcessor = new ResultImageProcessor();
resImgProThread = new Thread(resultImageProcessor);
}
#Override
public void onBarcodeDetected(final Barcode barcode) {
resultImageProcessor.setCameraBarcode(mCameraSource,barcode);
resImgProThread.start();
Intent intent = new Intent(this, ResultActivity.class);
intent.putExtra(BarcodeObject, barcode);
intent.putExtra(ResultCode, CommonStatusCodes.SUCCESS);
intent.putExtra(ResImgProcObject, resultImageProcessor);
startActivity(intent);
}
Result Image Processor:
public class ResultImageProcessor implements Serializable, Runnable {
private ResultActivity resultActivityContext;
ResultImageProcessor(){
this.resultActivityContext = null;
}
public void setResultActivity(ResultActivity resultActivity) {
this.resultActivityContext = resultActivity;
}
public void setCameraBarcode(CameraSource cameraSource, Barcode barCode){
mCameraSource = cameraSource;
barcode = barCode;
}
#Override
public void run() {
String picPath = ProcessImage(Obj..Attributes);
//wait until result activity context is not set
while(resultActivityContext == null){
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
resultActivityContext.onImageSaved(picPath);
}
}
Result Activity:
protected void onCreate(Bundle savedInstanceState) {
//...
data = getIntent();
Barcode barcode = data.getParcelableExtra(MainActivity.BarcodeObject);
ResultImageProcessor resultImageProcessor = data.getParcelableExtra(MainActivity.ResImgProcObject);
resultImageProcessor.setResultActivity(this);
}
//called from Result image processor
public void onImageSaved(String imagePath){
ImageView barImgView = findViewById(R.id.barcode_image);
Bitmap barcodeImage = BitmapFactory.decodeFile(imagePath);
barImgView.setImageBitmap(barcodeImage);
barImgView.invalidate();
}
With the above code, after invoking resultImageProcessor.startProcessing(), result activity is not launched nor runnable's run() method keeps busy in while loop. I traced them using logger. When I skip threading and pass image path to activity, everything goes fine beside being slow for activity switching.
Please indicate the problem or suggest better solution.
i think you are missing run in your runnable like this
new Runnable() {
#Override
public void run() {
String picPath = ProcessImage(Obj..Attributes);
//wait until result activity context is not set
while(resultActivityContext == null){}
resultActivityContext.onImageSaved(picPath);
}
}.run();
It turned out that the problem was in passing ResultImageProcessor object to ResultActivity intent as Parcelable. I followed a simple path of declaring resultActivityContext as static in ResultImageProcessor.
public class ResultImageProcessor implements Runnable {
public static ResultActivity resultActivityContext;
...
#Override
public void run() {
...
resultActivityContext.onImageSaved(picPath);
resultActivityContext = null;
}
}
and in ResultActivity:
ResultImageProcessor.resultActivityContext = this;

`CalledFromWrongThreadException` when calling QBChatService.login() method in QuickBlox API

I'm developing an IM app using the Quickblox API and I'm currently developing the Sign Up and Login features. Well, my problem is that everytime I try to login to the QBChatService by calling QBChatService.login() I'm getting this error from Log Cat:
E/Event: Could not dispatch event: class regmoraes.jusstalk.session.SessionEvents to subscribing class class regmoraes.jusstalk.session.LoginPresenter
E/Event: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I'm using MVP pattern and EventBus to send events from Models ( I called them Managers) to Presenters.
Here are my classes (interaction order between them at the end):
LoginActivity:
public class LoginActivity extends Activity implements LoginView, View.OnClickListener{
private AutoCompleteTextView mUserField;
private EditText mPasswordField;
private TextView mSignUpTextView;
private Button mLoginButton;
private ProgressBar mProgressBar;
private LoginUIPresenter loginPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mProgressBar = (ProgressBar) findViewById(R.id.login_progress);
mUserField = (AutoCompleteTextView) findViewById(R.id.email);
mPasswordField = (EditText) findViewById(R.id.password);
mLoginButton = (Button) findViewById(R.id.button_sign_in);
mLoginButton.setOnClickListener(this);
mSignUpTextView = (TextView) findViewById(R.id.textView_sign_up);
mSignUpTextView.setOnClickListener(this);
this.loginPresenter = new LoginPresenter(this);
}
#Override
public void showMessageDialog(List errors) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("chat login errors: " + errors).create().show();
}
#Override
public void startNewActivity(Class activity) {
Intent mIntent = new Intent(this, activity);
startActivity(mIntent);
finish();
}
#Override
public void showProgress(boolean show) {
if(show){
mProgressBar.setVisibility(View.VISIBLE);
mUserField.setVisibility(View.INVISIBLE);
mPasswordField.setVisibility(View.INVISIBLE);
mLoginButton.setVisibility(View.INVISIBLE);
}else{
mProgressBar.setVisibility(View.GONE);
mUserField.setVisibility(View.VISIBLE);
mPasswordField.setVisibility(View.VISIBLE);
}
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.button_sign_in:
loginPresenter.login(mUserField.getText().toString(),
mPasswordField.getText().toString());
break;
case R.id.textView_sign_up:
startNewActivity(SignUpActivity.class);
}
}
#Override
public void showToast(String message, int length) {
Toast.makeText(this, message,length).show();
}
}
LoginPresenter:
public class LoginPresenter implements LoginUIPresenter{
LoginView loginView;
SessionManager sessionManager;
public LoginPresenter(LoginView loginView) {
EventBus.getDefault().register(this);
/*...*/
}
#Override
public void login(String username, String password) {
loginView.showProgress(true);
sessionManager.login(username,password);
}
public void onEvent(SessionEvents sessionEvents){
switch (sessionEvents.getEvent()){
case SessionEvents.LOGIN_SUCCESSFULL:
sessionManager.loginToChatService();
break;
case SessionEvents.LOGIN_FAILED:
loginView.showProgress(false);
loginView.showToast("Problem when connecting", Toast.LENGTH_SHORT);
break;
case SessionEvents.CHAT_SERVICE_CONNECTED:
loginView.startNewActivity(MainActivity.class);
break;
default:break;
}
}
}
SessionManager:
public class SessionManagement implements SessionManager,ConnectionListener {
private String TAG = SessionManagement.class.getName();
private SharedPreferences mSharedPreferences;
private Context mContext;
private SessionEvents sessionEvents;
private QBUser currentUser;
public QBChatService qbChatService;
public SessionManagement(Context context) {
this.mContext = context;
this.mSharedPreferences = (mContext)
.getSharedPreferences("regmoraes.testapp", Context.MODE_PRIVATE);
initChatServiceIfNeeded();
this.sessionEvents = new SessionEvents();
this.qbChatService = QBChatService.getInstance();
}
/* .... */
private void initChatServiceIfNeeded() {
if (!QBChatService.isInitialized()) {
QBChatService.setDebugEnabled(true);
QBChatService.init(mContext);
QBChatService.getInstance().addConnectionListener(this);
}
}
#Override
public void login(final String username, final String password) {
final QBUser qbUser = new QBUser(username,password);
QBAuth.createSession(qbUser, new QBEntityCallbackImpl<QBSession>() {
#Override
public void onSuccess(QBSession qbSession, Bundle params) {
currentUser = qbUser;
currentUser.setId(qbSession.getId());
saveCredentials(currentUser.getLogin(), currentUser.getPassword());
sessionEvents.setEvent(SessionEvents.LOGIN_SUCCESSFULL);
EventBus.getDefault().post(sessionEvents);
}
#Override
public void onError(List<String> errors) {
sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
EventBus.getDefault().post(sessionEvents);
}
});
}
#Override
public void loginToChatService(){
qbChatService.login(currentUser, new QBEntityCallbackImpl() {
#Override
public void onSuccess() {
try {
qbChatService.startAutoSendPresence(30);
sessionEvents.setEvent(SessionEvents.CHAT_SERVICE_CONNECTED);
EventBus.getDefault().post(sessionEvents);
} catch (SmackException.NotLoggedInException e) {
e.printStackTrace();
}
}
#Override
public void onError(List errors) {
sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
EventBus.getDefault().post(sessionEvents);
}
});
}
}
This is how my classes interacts when user want to login:
User click on Sign In button in LoginActivity
LoginActivity calls LoginPresenter.signIn()
LoginPresenter calls SessionManager.login()
SessionManager send event LOGIN_SUCESSFULL to LoginPresenter
LoginPresenter calls SessionManager.loginToChatService()
ERROR
I know that the error is because of a Background Thread calling a UI Thread method, but the login method works well, only the loginToChat method that throws this error.
How could I fix this?
Thanks
As #logcat said:
It seems like the onEvent method is triggered by a background thread, unlike Android UI events which are already called on the UI thread for you.
And he was right, the onEvent method was triggered by the SessionManager.loginToChat() method, so to fix this, I had to make the onEvent be triggered on UI thread.
After searching the EvenBus Doc I saw this at the Delivery Threads and Threadmodes section:
EventBus can handle threading for you: events can be posted in threads different from the posting thread. (...)
In EventBus, you may define the thread that will call the event handling method onEvent by using a ThreadMode (...)
MainThread: Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly. Event handlers using this mode must return quickly to avoid blocking the main thread. Example:
// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}
So, what I had to do was to change the onEvent method of LoginPresenter to onEventMainThread! In that way, the LoginPresenter can handle the received event on UI thread.
Inside your loginToChatService() method, try to put the code of the login call inside a runOnUiThread call like this:
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
qbChatService.login(currentUser, new QBEntityCallbackImpl() {
...
}
});
activity should be an instance of Activity (could be this, depending on where your code is located).

Use Handler To Post In To UI Thread

I am working on an android application, that fetches image from Internet and show in the user interface. I am using RecyclerView for showing the image. I am planning to download the image using a separate thread. and update RecyclerView via the handler. I dont know wether this concept is correct or not, (I know AsyncTask, but for learning purpose I am trying to implement Handler.)
So I coded for the same as below
private void loadNewsThumbnailImage(ArrayList<DataItem> dataList) {
for (DataItem item : DataList) { //DataItem is the model class
loadThumbnailFromInternet(item);
}
}
private void loadThumbnailFromInternet(final DataItem dataItem) {
Thread imageDowloaderThread = new Thread(new Runnable() {
#Override
public void run() {
Bitmap bitmap = null;
try {
bitmap = getDataItemBitmap(dataItem.getmImageUrl());
dataItem.setmThumbnail(bitmap);
new Handler().post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
imageDowloaderThread.start();
}
I have executed this code but I am getting error, and application is terminated, I don't know why this is happening . please any one help me to sort it out. and explain what is the problem for the current code.
(Please do not suggest to use AsyncTask (I have tried that and it works fine))
UPDATE
Error getting :java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Your application is getting terminated because you are calling notifyDataSetChanged() from a non UI Thread.
Replace:
new Handler().post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
With this:
new Handler(Looper.getMainLooper()).post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
The thread you defined does not have a Looper, and no message queue,so you can not send message in this thread. AsyncTask has its own Looper which you can find it in its source code. This is handler defined in AsyncTask:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
#SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
#Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}

When I remove progress dialog, it works. Why?

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.

Check if a thread is running from the "mother" class

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?

Categories

Resources