Access activity context from runnable interface - java

I have this setup
MainActivity Class creates BTHandler which is a utility class. It passes activity context and application context.
public class MainActivity extends Activity implements View.OnClickListener{
protected void onCreate(Bundle savedInstanceState) {
...
currentBT = new BTHandle(this, MainActivity.this);
}
public Handler handler = new Handler() {
#Override
public void handleMessage(android.os.Message msg) {
...
super.handleMessage(msg);
}
};
}
This is the Utiliy class. It uses both context for some computation and when is required it creates ConnectThread, to start a connection with a BT device. It passes the activity context so ConnectThread can send a message through the Handler.
class BTHandle {
private final Context mainActivityContext;
private final Activity mainActivity;
...
public BTHandle(final Context context, final Activity activity){
mainActivityContext = context;
mainActivity = activity;
}
f(){
ConnectThread connectAsClient = new ConnectThread(mainActivityContext, BTDevice, mBluetoothAdapter, BTUuid);
new Thread(connectAsClient).start();
}
}
And Finally here comes the error. This is ConnectThread. I want to send a message from here to MainActivity. But compiler says it cant resolve handler. So I'm guessing the context doesn't come trough it.
public class ConnectThread implements Runnable {
private final Context mainActivity;
public ConnectThread(Context context, BluetoothDevice device, BluetoothAdapter adapter, UUID BT_UUID) {
...
mainActivity = context;
}
#Override
public void run() {
...
Message completeMessage = mainActivity.handler.obtainMessage(555, "CONNECTED");
completeMessage.sendToTarget();
}
}
How can I pass the context in the right way so ConnectThread can see handler?

You can either pass in the MainActivity object all the way down to ConnectRunnable i.e :
private final MainActivity mainActivity;
public ConnectThread(MainActivity context, BluetoothDevice device,
BluetoothAdapter adapter, UUID BT_UUID) {
...
mainActivity = context;
}
Or change your call to:
Message completeMessage =
((MainActivity)(Activity)mainActivity)).handler.obtainMessage(555, "CONNECTED");
Also, your handler is bound to leak memory.
Have a look at this for the fix: How to Leak a Context: Handlers & Inner Classes

You can also define a public and static MainActvity variable and use it in your other class :
public class MainActivity extends Activity implements View.OnClickListener{
public static MainActivity mainActivity;
protected void onCreate(Bundle savedInstanceState) {
...
mainActivity = this;
currentBT = new BTHandle(this, MainActivity.this);
}
public Handler handler = new Handler() {
#Override
public void handleMessage(android.os.Message msg) {
...
super.handleMessage(msg);
}
};
}
and in your other class :
public class ConnectThread implements Runnable {
private MainActivity mainActivity;
public ConnectThread(Context context, BluetoothDevice device, BluetoothAdapter adapter, UUID BT_UUID) {
...
mainActivity = MainActivity.mainActivity;
}
#Override
public void run() {
...
Message completeMessage = mainActivity.handler.obtainMessage(555, "CONNECTED");
completeMessage.sendToTarget();
}
}

Related

Call a method from MainActivity class from a non-activity class

I have troubles on calling the method update from MainActivity class in a the MSG0100 non-activity class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void update(boolean msg100Preselection){
if(msg100Preselection){
mExpandableListViewAdapter.setSelectedChild(-1);
mExpandableListViewAdapter.notifyDataSetChanged();
}
}
}
And this is my class where i want to call the update method of Mainactivity.
public class MSG0100{
boolean msg100Preselection=false;
pulic void onUpdate(){
msg100Preselection=true;
// Want to call my update method here
MainActivity activity= new MainActivity();
activity.update(msg100Preselection); //<-------- Using mainactiviy object crashes my app.
}
}
What you want is impossible as you dont have a pointer to your main activity.
The following statement is invalid.
MainActivity activity= new MainActivity();
You are not allowed to use the new operator to create an activity. That should be done using an intent.
There are several things you could do:
Move your update method in another class
OR
declare your update method as static and use it like this:
MainActivity.update(msg100Preselection);
Try using a callbackListener :-
In your MSG0100 class
public class MSG0100 {
boolean msg100Preselection = false;
private static OnUpdateListener mListener;
public static setListener(OnUpdateListener mListener) {
this.mListener = mListener;
}
public void onUpdate() {
msg100Preselection = true;
if (mListener != null)
mListener.onUpdate(msg100Preselection);
}
public interface OnUpdateListener()
{
void onUpdate ( boolean msg100Preselection);
}
}
In your MainActivity-
public class MainActivity extends AppCompatActivity, OnUpdateListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MSG0100.setListener(this)
}
#Override
public void onUpdate(boolean msg100Preselection) {
if (msg100Preselection) {
mExpandableListViewAdapter.setSelectedChild(-1);
mExpandableListViewAdapter.notifyDataSetChanged();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
MSG0100.setListener(null)
}
}
This way you won't have any memory leaks or crashes due to Activity being killed.

How to set broadcast listener interface in fragment?

I have service, which gets data from API and sends this data to BroadcastReceiver class. Also, I create interface OnReceiveListener, which used in Activity. Look at the code here:
Activity:
public class StartActivity extends AppCompatActivity
implements MyBroadcastReceiver.OnReceiveListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
receiver.setOnReceiveListener(this);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver,
new IntentFilter(MyBroadcastReceiver.START));
...
}
#Override
public void onReceive(Intent intent) {
// Do smth here
}
}
MyBroadcastReceiver:
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String START = "com.example.myapp.START";
public static final String GET_LINKS = "com.example.myapp.GET_LINKS";
private OnReceiveListener onReceiveListener = null;
public interface OnReceiveListener {
void onReceive(Intent intent);
}
public void setOnReceiveListener(Context context) {
this.onReceiveListener = (OnReceiveListener) context;
}
#Override
public void onReceive(Context context, Intent intent) {
if(onReceiveListener != null) {
onReceiveListener.onReceive(intent);
}
}
}
Service isn't important on this question.
---- Question ----
So, what's problem: I want to use this receiver in fragment, but when it sets context - I get exception "enable to cast". What I should to do on this case?
Here is my code in fragment:
public class MainFragment extends Fragment
implements MyBroadcastReceiver.OnReceiveListener {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
myBroadcastReceiver.setOnReceiveListener(getContext());
LocalBroadcastManager.getInstance(getContext()).registerReceiver(myBroadcastReceiver,
new IntentFilter(MyBroadcastReceiver.GET_LINKS));
}
#Override
public void onReceive(Intent intent) {
// Do smth here
}
}
Your MainFragment class implements your OnReceiveListener interface, not its Context as returned by getContext(). Instead of passing a Context object into setOnReceiveListener(), try directly passing an OnReceiveListener instance. Then your fragment and activity can both call setOnReceiveListener(this).
you don't need to dynamically register the receiver. i believe you must have registered it in manifest using <receiver> tag.
this is not required:
LocalBroadcastManager.getInstance(getContext()).registerReceiver(myBroadcastReceiver,
new IntentFilter(MyBroadcastReceiver.GET_LINKS));
and about callback registering listener, instead of using getContext() use MainFragment.this like this:
myBroadcastReceiver.setOnReceiveListener(MainFragment.this);
After searching for hours for the appropriate way to implement such a solution to this problem, I've found a way finally. It is based on RussHWolf's answer. The complete solution with code is below:
In this way, a setListener() method is exposed so that Fragment or Activity can set the listener by sending an instance of IStatusChangeListener.
public class StatusChangeReceiver extends BroadcastReceiver {
private IStatusChangeListener listener;
public void setListener(IStatusChangeListener listener) {
this.listener = listener;
}
#Override
public void onReceive(Context context, Intent intent) {
if (NetworkUtil.isNetworkConnected()) {
listener.onConnected();
} else {
listener.onDisconnected();
}
}
}
This is the interface:
public interface IStatusChangeListener {
void onConnected(String status);
void onDisonnected(String status);
}
Now, it is required to have an instance of IStatusChangeListener interface instead of implementing the IStatusChangeListener interface. And then, pass this instance of IStatusChangeListener to setListener() method.
public class MainFragment extends Fragment { //Not implementing the interface
private IStatusChangeListener listener = new IStatusChangeListener() {
#Override
void onConnected(String status) {
//some log here
}
#Override
void onDisonnected(String status) {
//some log here
}
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
StatusChangeReceiver r = new StatusChangeReceiver();
r.setListener(listener); // pass the IStatusChangeListener instance
LocalBroadcastManager.getInstance(getContext()).registerReceiver(r, new IntentFilter("connectionStatus"));
}
}
Note: Always use LocalBroadcastManager if you register BroadcastReceiver from Fragment.

Pass context to Async class

I'm calling an Async class from my main activity class. When the POST has been executed I want to return the result back to the main activity.
public class MainActivity extends Activity implements OnClickListener, AsyncResponse{
public Context context;
PostKey asyncTask = new PostKey(context);
public void onCreate(Bundle savedInstanceState) {
asyncTask.delegate = this;
}
public void onClick(View v) {
asyncTask.delegate = this;
new PostKey(context).execute(keyValue);
}
public void processFinish(String output){
//this you will received result fired from async class of onPostExecute(result) method.
Log.d("Result", output);
}
}
public class PostKey extends AsyncTask<String, String, String> {
public AsyncResponse delegate = null;
public Context context;
public PostKey(Context context){
this.context = context.getApplicationContext();
}
#Override
protected String doInBackground(String... params) {
return postData(params[0]);
}
#Override
protected void onPostExecute(String result){
this.context = context.getApplicationContext();
delegate = (AsyncResponse) context;
}
delegate.processFinish(result);
}
public interface AsyncResponse {
void processFinish(String output);
}
Whenever I try to run the app I immediately get a fatal error caused by a nullpointer exception. The nullpointer refers to the following:
public PostKey(Context context){
this.context = context.getApplicationContext();
}
&
PostKey asyncTask = new PostKey(context);
In the second case I can get that context is empty, but I have to pass the variable here.
Activity is already a Context, so you don't to keep a reference to it. Just use this. On the other hand the Activity has to go through its lifecycle before you can use the context. Remove
public Context context;
PostKey asyncTask = new PostKey(context);
and add
PostKey asyncTask = new PostKey(this);
in your onCreate. And please, add super.onCreate(savedInstanceState); as first thing in your onCreate
Hello Oryna you passing null context value as in parameter just replace these lines with this
new PostKey(context).execute(keyValue);
to
new PostKey(MainActivity.this).execute(keyValue);
and the constructor for the async task replace the code
public PostKey(Context context){
this.context = context.getApplicationContext();
}
with
public PostKey(Context context){
this.context = context;
}
try to use this.context = getApplicationContext(); instead

How to send result from AsyncTask from Activity1 to Activity2

In my Activity1, I have an AsyncTask that uploads to the server. Once this task is started, I want to start Activity, without waiting for the completion of AsyncTask. When the AsyncTask from Activity1 is completed, I want to update something in Activity2. After doing some searching, I've found multiple references/examples of using interfaces. But I ran into the following problem:
OnUploadCompleted Interface
public interface OnUploadCompleted {
void on UploadCompleted();
}
Activity2
public class Activity2 extends Activity implements OnUploadCompleted {
// all the usual activity code
#Override
public void onUploadCompleted() {
Toast.makeText(this, "Upload Done", ....
}
}
Activity1
public class Activity1 extends Activity {
// all the usual activity code
private class Upload extends AsyncTask<...> {
OnUploadCompleted listener;
public Upload(OnUploadCompleted listener) {
this.listener = listener;
}
// skipping doInBackground task
#Override
protected void onPostExecute(...) {
super.onPostExecute();
listener.onUploadCompleted();
}
}
void foo (...) {
OnUploadCompleted listener = new Activity2();
Upload upload = new Upload(listener);
upload.execute();
finish();
}
}
The problem I have is in the foo() function. the listener is a new instance of Activity2 class, but Activity2 hasn't been created yet. It will be created by the parent activity of Activity1, after the finish(). So, when the listener is actually called, the activity that it's "connected" to is null. In the onUploadCompleted(), when Toast is called, the "this" is null.
try sending Broadcasts to ACtivity2 from Activity1 when Activity1's AsyncTask completed...
public class MainActivity extends Activity {
public static final String ACTION_TASK_COMPLETED = "com.sample.project.action.ACTION_TASK_COMPLETED";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class DoTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// please wait. I am doing work
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// yay... work completed...
Intent intent = new Intent(ACTION_TASK_COMPLETED);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
}
}
}
public class SecondActivity extends Activity {
private TaskReceiver taskReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter intentFilter = new IntentFilter(MainActivity.ACTION_TASK_COMPLETED);
taskReceiver = new TaskReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(taskReceiver, intentFilter);
}
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(taskReceiver);
}
private void onUploadImage() {
// uploading completed...
}
private class TaskReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
onUploadImage();
}
}
}

Implementing a listener in android application class with the event listener in a FragmentActivity

Just started programming and I'm coding my first app. I've been wrecking my brain over this for long enough. I picked this up from tutorial where a listener is implemented in a ListFragment and the event listener is in a FragmentActivity that hosts the ListFragment. Here is how it is in the example (showing only the relevant part):
MyList class:
public class MyList extends ListFragment{
OnSomeItemSelectedListener mCallback;
public interface OnSomeItemSelectedListener {
public void onSomeItemSelectedListener(int position, long id);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (OnSomeItemSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnSomeItemSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
mCallback.onSomeItemSelectedListener(position, id);
}
}
MainActivity class:
public class MainActivity extends FragmentActivity implements
MyList.OnSomeItemSelectedListener{
#Override
public void onSomeItemSelectedListener(int position, long id) {
//some stuff done here when clicked on a item from the listfragment
}
}
The above works flawlessly.
I'm trying to implement this in an application class to notify the event listener (a FragmentActivity) when an AsyncTask has finished. This is what I did in in the application class:
MyApplication class:
public class MyApplication extends Application {
OnFetchCompleteListener mCallback;
public interface OnFetchCompleteListener {
public void onFetchCompleteListener();
}
public synchronized void fetchUpdates() {
String[] mURLs = getURLs();
new DownloadDataAsyncTask().execute(mURLs[0], mURLs[1]);
}
private class DownloadDataAsyncTask extends
AsyncTask<String, Void, JSONObject[]> {
#Override
protected JSONObject[] doInBackground(String... params) {
//Some stuff
return mJSON;
}
#Override
protected void onPostExecute(JSONObject[] result) {
// Informing listeners on fetchData complete
mCallback.onFetchCompleteListener();
}
}
}
How do I instantiate the mCallback in the application class? Without this,
mCallback = (OnSomeItemSelectedListener) activity;
I get a null pointer exception from the application class.
Many thanks
I think you can instantiate mCallback from the Activity instead of from the Application.
In your activity's onCreate() or onResume() method, you can write:
MyApplication myApplication = getApplication();
myApplicaton.mCallback = this;
but make sure your activity have implemented OnFetchCompleteListener (I don't why you typed OnSomeItemSelectedListener in the last piece of code.)

Categories

Resources