Android ClassCastException by referencing fragment in viewmodel


Android ClassCastException by referencing fragment in viewmodel



I was having some problem when trying to reference the fragment in my viewmodel class. Here is the part where my fragment call the viewmodel:


@Click(R.id.buttonExport)
void buttonExportClicked(View v){
SummaryViewModel summaryViewModel = ViewModelProviders.of(this).get(SummaryViewModel.class);
summaryViewModel.export(webViewResult);
}



In the viewmodel where I tried to access the context as well as Activity:


public void exportSelfTestSummary(WebView webViewResult) {
Activity context = (Activity) getApplication().getApplicationContext();
final ProgressDialog progressDialog=new ProgressDialog(context);
progressDialog.show();
PdfView.createWebPrintJob(context, webViewResult, directory, fileName, new PdfView.Callback() {
....
}



And the error message I am getting:


07-02 02:34:01.922 3563-3563/com.mainapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mainapp, PID: 3563
java.lang.ClassCastException: com.mainapp.App cannot be cast to android.app.Activity
at com.mainapp.viewmodel.SummaryViewModel.export(SummaryViewModel.java:218)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)



Any ideas? Thanks!



Edit


@Inject
public SummaryViewModel(@NonNull Application application) {
super(application);
}




4 Answers
4



The error says it clear enough, you cannot cast Application to Activity


Application


Activity



What you are doing is get the application context, which will return an Application class:


Application


Activity context = (Activity) getApplication().getApplicationContext();



If you are inside a fragment, just simply use fragment.getActivity()


fragment.getActivity()





How can I change this line: Activity context = (Activity) getApplication().getApplicationContext(); to fragment.getActivity? As in I do not want to pass the fragment as parameter into that function
– hyperfkcb
Jul 2 at 2:45






How do you construct your ViewModel now? Post part of its code. Besides, where is the method getApplication come from? As far as I know, getApplication method only available in Activity
– Tam Huynh
Jul 2 at 2:50



ViewModel


getApplication


getApplication


Activity





Edited question!
– hyperfkcb
Jul 2 at 3:01



You are getting a ClassCastException because you are trying to cast the application into an Activity.


Activity context = (Activity) getApplication().getApplicationContext();



getApplication() method returns the App class and so does the getApplicationContext() method, which cannot be used to get the Activity.



You should never use a reference to an Activity or Fragment in your ViewModel. Your UI should never be modified from your ViewModel. ViewModel should only contain data used to modify your UI.



As an example:



Fragment/Activity:


SummaryViewModel summaryViewModel = ViewModelProviders.of(this).get(SummaryViewModel.class);
LiveData<WebResult> data = summaryViewModel.getWebViewDataInBackgroundThread();

//observe on this data for changes in your activity/fragment
data.observe(this, Observer {
@Override
public void onChanged(@Nullable final WebResult result) {
// Update the UI,
Activity context = (Activity) getApplication().getApplicationContext();
final ProgressDialog progressDialog=new ProgressDialog(context);
progressDialog.show();
PdfView.createWebPrintJob(context, webViewResult, directory, fileName, new PdfView.Callback() {
....
}
})



In your ViewModel:


public LiveData<WebResult> getWebViewDataInBackgroundThread() {
// getdata and return as LiveData
}



Read more on LiveData and ViewModel



Alternatively you can reach the Activity digging through parent views:


Activity act = (Activity) webViewResult.getParent().getParent().....so on



It not a good practice to use Context and Activity Lifecycle related staff specially view inside ViewModel. ViewModel will only hold data and make a connection between data and view. Create ProgressDialog inside Activity or Fragment. Use ViewModel for your business logic. Create a LiveData for ProgressDialog state in ViewModle. Change LiveData value and observe it in Activity or Fragment. So you can decide when to show ProgressDialog or when to hide it. Your view is inside Activity or Fragment and there is no possibility of MemoryLeak.


Context


ViewModel


ViewModel


ProgressDialog


Activity


Fragment


ViewModel


LiveData


ProgressDialog


ViewModle


LiveData


Activity


Fragment


ProgressDialog


Activity


Fragment



As @tam-huynh mentioned your problem is you trying to cast Application Context to Activity that is not valid.


Activity



Create LiveData in ViewModel


LiveData


ViewModel


private MutableLiveData<Integer> progressDialogState;

public LiveData<Integer> getProgressDialogStateAsObservable(){
return progressDialogState;
}

public void setProgressDialogState(int value){
progressDialogState.setValue(value);
}


public void exportSelfTestSummary(WebView webViewResult) {
//Activity context = (Activity) getApplication().getApplicationContext();
//final ProgressDialog progressDialog=new ProgressDialog(context);
//progressDialog.show();
setProgressDialogState(1)
PdfView.createWebPrintJob(context, webViewResult, directory, fileName, new PdfView.Callback() {
....

// after work done set value to 0
//setProgressDialogState(0)
}



Observe it in Activity or Fragment


Activity


Fragment


viewModel.getProgressDialogStateAsObservable().observe(getActivity(), state -> {
if (state == 1) {
// .....
// show
}else if(state == 0){
// hide
}
});



Learn about Android Architecture Components from here






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Rothschild family

Cinema of Italy