I have little loading screen which has TextField, when loading screen is created it launches new Thread. That thread has 3 things to do, 1) Create new object to scanning class and run it's scanning method 2)in scanning method were it gets tricky it should update status with setStatus and latter call method updateWidget but it returns "android.view.View android.view.Window.findViewById(int)' on a null object reference" and 3) return all packages using setPackages. So how can I reach widget from thread, also extra question about part 3 which I mentioned is that right way to do ?
public class LoadingScreen extends Activity {
private String status="";
List<ApplicationInfo> packages;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.loadingscreen_layout);
updateWidget();
try {
startLoadingScreen();
}catch (Exception e){
Log.e("Scanner","Scanner",e);
}
}
public void startLoadingScreen(){
new Thread(new Runnable() {
#Override
public void run() {
Scanner scanner = new Scanner();
setPackages(scanner.Scan());
changeIntent();
finish();
}
});
}
public void setStatus(String status) {
this.status = status;
}
void updateWidget(){
TextView scanStatus = findViewById(R.id.statusView);
scanStatus.setText(status);
}
private void changeIntent(){
Intent intent = new Intent(this,DisplayClass.class);
startActivity(intent);
}
public void setPackages(List<ApplicationInfo> packages) {
this.packages = packages;
}
}
Scanning Class
public class Scanner extends Activity{
private LoadingScreen loadingScreen = new LoadingScreen();
List <android.content.pm.ApplicationInfo> Scan(){
loadingScreen.setStatus("Scanning");
loadingScreen.updateWidget();
final PackageManager pm = getPackageManager();
//get a list of installed apps.
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
return packages;
}
}
In your new thread run() method you can try to update ui by using runOnUiThread
try {
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//update ui views
btn.setText("some_text");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
Alternatively you can use AsyncTask class to update ui elements from non ui thread.
Related
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.
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;
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).
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296513, class xyz.ScrollDetectableListView) with Adapter(class android.widget.HeaderViewListAdapter)]
I am getting above exception sometimes while scrolling through the dynamic listview and then clicking on item.I researched a lot but unable to find the exact reason that why i am getting this error sometimes and how it can be resolved?
private ScrollDetectableListView mFListView;
public FAdapter mFAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_feed_view, container, false);
View headerView = getActivity().getLayoutInflater().inflate(R.layout.view_feed_header, null);
try{
mFListView = (ScrollDetectableListView) rootView.findViewById(R.id.feed_list_view);
mFContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.feed_container);
mFListView.addHeaderView(headerView);
mFListView.setEmptyView(rootView.findViewById(R.id.empty_view));
mFContainer.setColorSchemeResources(R.color.green, R.color.pink, R.color.fbcolor,
R.color.instagramcolor, R.color.googlecolor, R.color.flickrcolor);
mFView = getActivity().getLayoutInflater().inflate(R.layout.view_footer, null);
ImageView rotateImageView = (ImageView) mFooterView.findViewById(R.id.spinner);
Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate);
rotation.setFillAfter(false);
rotateImageView.startAnimation(rotation);
mFContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh()
{
initializeFListView();
}
});
initializeFListView();
mProgressDialog.setVisibility(View.VISIBLE);
mHActivity.setDataChangedListener(new DataChangedListener() {
#Override
public void onDataChanged() {
mFContainer.setRefreshing(true);
mProgressDialog.setVisibility(View.GONE);
initializeFListView();
}
});
}catch(Exception e){}
return rootView;
}
public void initializeFListView()
{
FApi.getTrending(getActivity(), xyz, new APIResponseListener() {
#Override
public void onResponse(Object response) {
setFeedAdapter((List<Video>) response);
}
#Override
public void onError(VolleyError error) {
if (error instanceof NoConnectionError) {
String errormsg = getResources().getString(R.string.no_internet_error_msg);
Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
}
}
});
}
private void setFAdapter(List<Video> response)
{try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
mFListView.setAdapter(mFAdapter);
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
My suggestion try to set ur list adapter on UI Thread,,,
private void setFAdapter(List<Video> response)
{
try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
mFListView.setAdapter(mFAdapter);
}
});
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
Keep one singleton class object in hand. So that you can synchronize two thread on it. Care to be taken to not to block the ui thread.
Reduce number of interfaces to only one method to start preparing data for your list and only one method to call your notifydatasetchanged/setAdapter on list.
Means there should be only one method like prepareData() which will be executed by a background thread. synchronise this method on your singleton object.
MyListAdaper adapter = null;
// Call this from a background thread
public void prepareData() {
synchronized (SingleTonProvider.getInstance()) {
List<AnyDataTypeYouWant> data = null;
// populate data here by your application logic.
adapter = new MyListAdaper(data);
}
}
And have only one method to refresh list.
// Also Call this from a background thread only
public void refreshList() {
synchronized (SingleTonProvider.getInstance()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mFListView.setAdapter(adapter);
}
});
}
}
have no other code on any place to prepare data and set data on list.
Call the methods I mentioned from a background thread only.
I just gave general solution to your problem. You have to work on your specific case by yourself.
I have created IntentService class and performing asyncTask but getting exception when onPreExecute() is called at this code line pDialog.show();
AsyncHandlerService Class ---
public class AsyncHandlerService extends IntentService{
ProgressDialog pDialog;
HttpPost post;
HttpResponse response;
Context ctx;
public AsyncHandlerService() {
super("AsyncHandlerService");
ctx = this;
}
#Override
protected void onHandleIntent(Intent intent) {
new LoadDeviceInfo().execute();
}
class LoadDeviceInfo extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(ctx);
pDialog.setMessage("Updating device info...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show(); //Exception here..
}
protected String doInBackground(String... args) {
}
protected void onPostExecute(String file_url) {
pDialog.dismiss();
}
UPDATE:
I am calling the IntentService in the broadcast receiver that has the intent filter of android.intent.action.PACKAGE_REPLACED defined in android manifest. The code ---
public class OnUpgradeBroadcastReceiver extends BroadcastReceiver {
Context activity;
#Override
public void onReceive(final Context context, final Intent intent) {
activity = context;
Intent msgIntent = new Intent(activity, AsyncHandlerService.class);
activity.startService(msgIntent);
}
}
Error Log:
com.testapp.main fatal error : Unable to add window --
token null is not for an application
android.view.WindowManager$BadTokenException: Unable to add window --
token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:588)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:326)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
at android.view.WindowManagerImpl$CompatModeWrapper.
addView(WindowManagerImpl.java:149)
at android.app.Dialog.show(Dialog.java:277)
at com.testapp.main.AsyncHandlerService$LoadDeviceInfo.
onPreExecute(AsyncHandlerService.java:62)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
at android.os.AsyncTask.execute(AsyncTask.java:534)
First, IntentService already uses a background thread. You do not need another background thread. Do the work that needs to be done in the background in onHandleIntent().
Second, a Service cannot display a Dialog. Instead, let the UI layer of your app know that the work was done via a message on an event bus (e.g., LocalBroadcastManager, greenrobot's EventBus, Square's Otto). If the UI layer does not handle the event, your service can raise a Notification or otherwise let the user know about the work that was done, if that is needed.
Service isn't a UI thread.
Since you try to display a ProgressDialog from a service context, it can't be completed.
Try this solution:
https://stackoverflow.com/a/4369755/1405268
If for whatever reason you really really really want to use an AsyncTask (e.g. you've set up your framework to use AsyncTask to make calls to some web api) you can always use wait/notify such as:
public class GetCacheIntentService extends DebuggableIntentService implements ApiAsyncTask.Callback {
private static final String ACTION_GET_CACHE = "action.GET_CACHE";
private static final String EXTRA_INT_START = "extras.START";
private static final String EXTRA_INT_LIMIT = "extras.LIMIT";
private static final int API_GET_CACHE = 0;
private final Object mApiCallLock = new Object();
private GetCacheResponse getCacheResponse;
public GetCacheIntentService() {
super("GetCacheIntentService");
setIntentRedelivery(true);
}
public static void startServiceActionGetCache(Context context, int start, int limit) {
Intent intent = new Intent(context, GetCacheIntentService.class);
intent.setAction(ACTION_GET_CACHE);
intent.putExtra(EXTRA_INT_START, start);
intent.putExtra(EXTRA_INT_LIMIT, limit);
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
String action = intent.getAction();
if (ACTION_GET_CACHE.equals(action)) {
int start = intent.getIntExtra(EXTRA_INT_START, 0);
int limit = intent.getIntExtra(EXTRA_INT_LIMIT, 100);
getCache(start, limit);
}
}
private void getCache(int start, int limit) {
GetCacheTask task = new GetCacheTask(this, API_GET_CACHE);
task.setStart(start);
task.setLimit(limit);
task.execute();
synchronized (mApiCallLock) {
try {
mApiCallLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
processResponse(mGetCacheResponse);
}
public void processResponse(GetCacheResponse response) {
// do something
}
#Override
public void onRequestFailed(int id, ApiResponse apiResponse) {
synchronized (mApiCallLock) {
switch (id) {
case API_GET_CACHE:
break;
}
mApiCallLock.notify();
}
}
#Override
public void onRequestSuccess(int id, ApiResponse response) {
synchronized (mApiCallLock) {
switch (id) {
case API_GET_CACHE:
mGetCacheResponse = (GetCacheResponse) response;
break;
}
mApiCallLock.notify();
}
}
}
this is quite ugly though :(
Not a good practise to call Asynctask from an Intent service. If you need to do spin other thread from IntentService consider using Executor.
ExecutorService es = Executors.newFixedThreadPool(5);
es.execute(new Runnable() {
#Override
public void run() {
}
});
es.execute(new Runnable() {
#Override
public void run() {
}
});
es.shutdown();
es.awaitTermination(1, TimeUnit.HOURS);
It is not a good practice to use AsyncTask inside IntentService sub-classes or even JobIntentService sub-classes. In the case of JobIntentServices it causes crash too.